From 1f9a4180a3b54f85f37919ba4ca709f8e8250bb6 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sun, 22 May 2016 22:39:26 +0800 Subject: macro_rules - Rework pattern matching to not use try-catch --- src/ast/ast.cpp | 3 + src/ast/ast.hpp | 21 +- src/ast/crate.cpp | 2 +- src/ast/crate.hpp | 4 +- src/expand/macro_rules.cpp | 8 +- src/expand/macro_rules.hpp | 2 +- src/expand/mod.cpp | 2 +- src/hir/from_ast.cpp | 2 +- src/hir/hir.hpp | 2 +- src/macro_rules/eval.cpp | 639 +++++++++++++++++++++++++++ src/macro_rules/macro_rules.hpp | 153 +++++++ src/macro_rules/macro_rules_ptr.hpp | 31 ++ src/macro_rules/mod.cpp | 203 +++++++++ src/macro_rules/parse.cpp | 509 ++++++++++++++++++++++ src/macro_rules/pattern_checks.hpp | 10 + src/macros.cpp | 846 ------------------------------------ src/macros.hpp | 137 ------ src/main.cpp | 5 +- src/parse/common.hpp | 2 - src/parse/expr.cpp | 1 - src/parse/macro_rules.cpp | 551 ----------------------- src/parse/root.cpp | 1 - 22 files changed, 1575 insertions(+), 1559 deletions(-) create mode 100644 src/macro_rules/eval.cpp create mode 100644 src/macro_rules/macro_rules.hpp create mode 100644 src/macro_rules/macro_rules_ptr.hpp create mode 100644 src/macro_rules/mod.cpp create mode 100644 src/macro_rules/parse.cpp create mode 100644 src/macro_rules/pattern_checks.hpp delete mode 100644 src/macros.cpp delete mode 100644 src/macros.hpp delete mode 100644 src/parse/macro_rules.cpp (limited to 'src') diff --git a/src/ast/ast.cpp b/src/ast/ast.cpp index 23e9bfea..84218591 100644 --- a/src/ast/ast.cpp +++ b/src/ast/ast.cpp @@ -313,6 +313,9 @@ void Module::add_submod(bool is_public, ::std::string name, Module mod, MetaItem DEBUG("mod.m_name = " << name << ", attrs = " << attrs); this->add_item( is_public, mv$(name), Item::make_Module({mv$(mod)}), mv$(attrs) ); } +void Module::add_macro(bool is_exported, ::std::string name, MacroRulesPtr macro) { + m_macros.push_back( Named( mv$(name), mv$(macro), is_exported ) ); +} void Module::prescan() { diff --git a/src/ast/ast.hpp b/src/ast/ast.hpp index b7de73ac..d1e8c6a2 100644 --- a/src/ast/ast.hpp +++ b/src/ast/ast.hpp @@ -19,17 +19,18 @@ #include "../parse/tokentree.hpp" #include "../types.hpp" -#include "../macros.hpp" #include -#include "pattern.hpp" -#include "attrs.hpp" -#include "expr.hpp" -#include "macro.hpp" -#include "item.hpp" +#include +#include +#include +#include +#include #include "generics.hpp" +#include + namespace AST { class Crate; @@ -551,7 +552,7 @@ private: ::std::vector m_anon_modules; ::std::vector< NamedNS > m_macro_import_res; // Vec of imported macros (not serialised) - ::std::vector< Named > m_macros; + ::std::vector< Named > m_macros; public: char m_index_populated = 0; // 0 = no, 1 = partial, 2 = complete @@ -599,9 +600,7 @@ public: void add_neg_impl(ImplDef impl) { m_neg_impls.emplace_back( ::std::move(impl) ); } - void add_macro(bool is_exported, ::std::string name, MacroRules macro) { - m_macros.push_back( Named( move(name), move(macro), is_exported ) ); - } + void add_macro(bool is_exported, ::std::string name, MacroRulesPtr macro); void add_macro_import(::std::string name, const MacroRules& mr) { m_macro_import_res.push_back( NamedNS( mv$(name), &mr, false ) ); } @@ -641,7 +640,7 @@ public: ::std::vector& macro_invs() { return m_macro_invocations; } - const NamedList& macros() const { return m_macros; } + const NamedList& macros() const { return m_macros; } const ::std::vector > macro_imports_res() const { return m_macro_import_res; } diff --git a/src/ast/crate.cpp b/src/ast/crate.cpp index d40879f3..500598b0 100644 --- a/src/ast/crate.cpp +++ b/src/ast/crate.cpp @@ -325,7 +325,7 @@ const MacroRules* ExternCrate::find_macro_rules(const ::std::string& name) { auto i = m_mr_macros.find(name); if(i != m_mr_macros.end()) - return &i->second; + return &*i->second; return nullptr; } diff --git a/src/ast/crate.hpp b/src/ast/crate.hpp index dbb9ca0d..b88b9307 100644 --- a/src/ast/crate.hpp +++ b/src/ast/crate.hpp @@ -79,7 +79,7 @@ private: class ExternCrate: public Serialisable { - ::std::map< ::std::string, MacroRules > m_mr_macros; + ::std::map< ::std::string, MacroRulesPtr > m_mr_macros; //::MIR::Module m_root_module; @@ -87,6 +87,8 @@ class ExternCrate: public: ExternCrate(); ExternCrate(const char *path); + ExternCrate(const ExternCrate&) = delete; + ExternCrate(ExternCrate&&) = default; const MacroRules* find_macro_rules(const ::std::string& name); diff --git a/src/expand/macro_rules.cpp b/src/expand/macro_rules.cpp index 968bf5f9..1689746c 100644 --- a/src/expand/macro_rules.cpp +++ b/src/expand/macro_rules.cpp @@ -4,6 +4,7 @@ #include "../ast/ast.hpp" #include "../parse/common.hpp" #include "macro_rules.hpp" +#include class CMacroRulesExpander: public ExpandProcMacro @@ -15,9 +16,10 @@ class CMacroRulesExpander: if( ident == "" ) ERROR(sp, E0000, "macro_rules! requires an identifier" ); + DEBUG("Parsing macro_rules! " << ident); TTStream lex(tt); auto mac = Parse_MacroRules(lex); - mod.add_macro( false, ident, mac ); + mod.add_macro( false, ident, mv$(mac) ); return ::std::unique_ptr( new TTStreamO(TokenTree()) ); } @@ -45,7 +47,7 @@ class CMacroUseHandler: { if( mr.name == name ) { DEBUG("Imported " << mr.name); - mod.add_macro_import( mr.name, mr.data ); + mod.add_macro_import( mr.name, *mr.data ); goto _good; } } @@ -67,7 +69,7 @@ class CMacroUseHandler: for( const auto& mr : submod.macros() ) { DEBUG("Imported " << mr.name); - mod.add_macro_import( mr.name, mr.data ); + mod.add_macro_import( mr.name, *mr.data ); } for( const auto& mri : submod.macro_imports_res() ) { diff --git a/src/expand/macro_rules.hpp b/src/expand/macro_rules.hpp index b4e6e1e0..fc4a2851 100644 --- a/src/expand/macro_rules.hpp +++ b/src/expand/macro_rules.hpp @@ -4,7 +4,6 @@ #pragma once #include -#include "../macros.hpp" namespace AST { class Expr; @@ -12,5 +11,6 @@ namespace AST { } class TokenTree; class TokenStream; +class MacroRules; extern ::std::unique_ptr Macro_Invoke(const char* name, const MacroRules& rules, const TokenTree& tt, AST::Module& mod); diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index 3f0911fe..0cb2c9c7 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -105,7 +105,7 @@ void Expand_Attrs(const ::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& if( input_ident != "" ) ERROR(mi_span, E0000, "macro_rules! macros can't take an ident"); - auto e = Macro_Invoke(name.c_str(), mr.data, input_tt, mod); + auto e = Macro_Invoke(name.c_str(), *mr.data, input_tt, mod); return e; } } diff --git a/src/hir/from_ast.cpp b/src/hir/from_ast.cpp index 7ab503cf..9595ca02 100644 --- a/src/hir/from_ast.cpp +++ b/src/hir/from_ast.cpp @@ -20,7 +20,7 @@ // - Extract macros from root module for( const auto& mac : crate.m_root_module.macros() ) { //if( mac.data.export ) { - macros.insert( ::std::make_pair( mac.name, mac.data ) ); + macros.insert( ::std::make_pair( mac.name, mv$(*mac.data) ) ); //} } for( const auto& mac : crate.m_root_module.macro_imports_res() ) { diff --git a/src/hir/hir.hpp b/src/hir/hir.hpp index ff0d82a9..cc63626f 100644 --- a/src/hir/hir.hpp +++ b/src/hir/hir.hpp @@ -12,7 +12,7 @@ #include -#include // DAMNIT - Why can't I have it be incomplete +#include // DAMNIT - Why can't I have it be incomplete #include #include diff --git a/src/macro_rules/eval.cpp b/src/macro_rules/eval.cpp new file mode 100644 index 00000000..44900807 --- /dev/null +++ b/src/macro_rules/eval.cpp @@ -0,0 +1,639 @@ +/* + */ +#include +#include "macro_rules.hpp" +#include +#include +#include +#include +#include "pattern_checks.hpp" + +class ParameterMappings +{ + // MultiMap (layer, name) -> TokenTree + // - Multiple values are only allowed for layer>0 + typedef ::std::pair t_mapping_block; + struct Mapping + { + t_mapping_block block; + ::std::vector entries; + friend ::std::ostream& operator<<(::std::ostream& os, const Mapping& x) { + os << "(" << x.block.first << ", " << x.block.second << "): '" << x.entries << "')"; + return os; + } + }; + + struct less_cstr { + bool operator()(const char *a, const char *b) const { return ::std::strcmp(a,b) < 0; } + }; + + + ::std::vector m_mappings; + unsigned m_layer_count; + + //::std::map m_map; + ::std::map<::std::string, Mapping*> m_map; +public: + ParameterMappings(): + m_layer_count(0) + { + } + ParameterMappings(ParameterMappings&&) = default; + + const ::std::vector& mappings() const { return m_mappings; } + + void dump() const { + DEBUG("m_mappings = {" << m_mappings << "}"); + DEBUG("m_map = {" << m_map << "}"); + } + + size_t layer_count() const { + return m_layer_count+1; + } + + void insert(unsigned int layer, unsigned int name_index, TokenTree data) { + if(layer > m_layer_count) + m_layer_count = layer; + + if( name_index >= m_mappings.size() ) { + m_mappings.resize( name_index + 1 ); + } + auto& mapping = m_mappings[name_index]; + + if( mapping.entries.size() == 0 ) { + mapping.block.first = layer; + } + else if( mapping.block.first != layer) { + throw ParseError::Generic(FMT("matching argument #'"<& new_names) { + m_map.clear(); + + if( new_names.size() < m_mappings.size() ) { + BUG(Span(), "Macro parameter name mismatch - len["< layer ) { + throw ParseError::Generic( FMT("'" << name << "' is still repeating at this layer") ); + } + else if( idx >= e.entries.size() ) { + DEBUG("Not enough mappings for name " << name << " at layer " << layer << " PI " << iteration); + return nullptr; + } + else { + DEBUG(name << " #" << idx << ": " << e.entries[idx]); + return &e.entries[idx]; + } + } + unsigned int count(unsigned int layer, unsigned int iteration, const char *name) const + { + const auto it = m_map.find( name ); + if( it == m_map.end() ) { + DEBUG("("<second; + + if( e.block.first < layer ) { + DEBUG(name<<" higher layer (" << e.block.first << ")"); + return UINT_MAX; + } + else if( e.block.first > layer ) { + //throw ParseError::Generic( FMT("'" << name << "' is still repeating at this layer") ); + // ignore mismatch + return UINT_MAX; + } + else { + return e.entries.size(); + } + } +}; + +class MacroExpander: + public TokenStream +{ +public: + +private: + const RcString m_macro_filename; + + const ::std::string m_crate_name; + const ::std::vector& m_root_contents; + const ParameterMappings m_mappings; + + + struct t_offset { + unsigned read_pos; + unsigned loop_index; + unsigned max_index; + }; + /// Layer states : Index and Iteration + ::std::vector< t_offset > m_offsets; + ::std::vector< size_t > m_layer_iters; + + /// Cached pointer to the current layer + const ::std::vector* m_cur_ents; // For faster lookup. + + Token m_next_token; // used for inserting a single token into the stream + ::std::unique_ptr m_ttstream; + +public: + MacroExpander(const MacroExpander& x) = delete; + //MacroExpander(const MacroExpander& x): + // m_macro_name( x.m_macro_name ), + // m_crate_name(x.m_crate_name), + // m_root_contents(x.m_root_contents), + // m_mappings(x.m_mappings), + // m_offsets({ {0,0,0} }), + // m_cur_ents(&m_root_contents) + //{ + // prep_counts(); + //} + MacroExpander(const ::std::string& macro_name, const ::std::vector& contents, ParameterMappings mappings, ::std::string crate_name): + m_macro_filename( FMT("Macro:" << macro_name) ), + m_crate_name( mv$(crate_name) ), + m_root_contents(contents), + m_mappings( mv$(mappings) ), + m_offsets({ {0,0,0} }), + m_cur_ents(&m_root_contents) + { + prep_counts(); + } + + virtual Position getPosition() const override; + virtual Token realGetToken() override; +private: + const MacroRuleEnt& getCurLayerEnt() const; + const ::std::vector* getCurLayer() const; + void prep_counts(); + unsigned int count_repeats(const ::std::vector& ents, unsigned layer, unsigned iter); +}; + +void Macro_InitDefaults() +{ +} + +bool Macro_TryPattern(TTStream& lex, const MacroPatEnt& pat) +{ + DEBUG("pat = " << pat); + Token tok; + switch(pat.type) + { + case MacroPatEnt::PAT_TOKEN: { + GET_TOK(tok, lex); + bool rv = (tok == pat.tok); + PUTBACK(tok, lex); + return rv; + } + case MacroPatEnt::PAT_LOOP: + if( pat.name == "*" ) + return true; + return Macro_TryPattern(lex, pat.subpats[0]); + case MacroPatEnt::PAT_BLOCK: + return LOOK_AHEAD(lex) == TOK_BRACE_OPEN; + case MacroPatEnt::PAT_IDENT: + return LOOK_AHEAD(lex) == TOK_IDENT; + case MacroPatEnt::PAT_TT: + return LOOK_AHEAD(lex) != TOK_EOF; + case MacroPatEnt::PAT_PATH: + return is_token_path( LOOK_AHEAD(lex) ); + case MacroPatEnt::PAT_TYPE: + return is_token_type( LOOK_AHEAD(lex) ); + case MacroPatEnt::PAT_EXPR: + return is_token_expr( LOOK_AHEAD(lex) ); + case MacroPatEnt::PAT_STMT: + return is_token_stmt( LOOK_AHEAD(lex) ); + case MacroPatEnt::PAT_PAT: + return is_token_pat( LOOK_AHEAD(lex) ); + case MacroPatEnt::PAT_META: + return LOOK_AHEAD(lex) == TOK_IDENT; + } + throw ParseError::Todo(lex, FMT("Macro_TryPattern : " << pat)); +} + +bool Macro_HandlePattern(TTStream& lex, const MacroPatEnt& pat, unsigned int layer, ParameterMappings& bound_tts) +{ + TRACE_FUNCTION_F("layer = " << layer); + Token tok; + TokenTree val; + + switch(pat.type) + { + case MacroPatEnt::PAT_TOKEN: + DEBUG("Token " << pat.tok); + GET_CHECK_TOK(tok, lex, pat.tok.type()); + break; + case MacroPatEnt::PAT_LOOP: + //case MacroPatEnt::PAT_OPTLOOP: + { + unsigned int match_count = 0; + DEBUG("Loop"); + for(;;) + { + if( ! Macro_TryPattern(lex, pat.subpats[0]) ) + { + DEBUG("break"); + break; + } + for( unsigned int i = 0; i < pat.subpats.size(); i ++ ) + { + if( !Macro_HandlePattern(lex, pat.subpats[i], layer+1, bound_tts) ) { + DEBUG("Ent " << i << " failed"); + return false; + } + } + match_count += 1; + DEBUG("succ"); + if( pat.tok.type() != TOK_NULL ) + { + if( GET_TOK(tok, lex) != pat.tok.type() ) + { + lex.putback( mv$(tok) ); + break; + } + } + } + DEBUG("Done (" << match_count << " matches)"); + break; } + + case MacroPatEnt::PAT_TT: + DEBUG("TT"); + if( GET_TOK(tok, lex) == TOK_EOF ) + throw ParseError::Unexpected(lex, TOK_EOF); + else + PUTBACK(tok, lex); + val = Parse_TT(lex, false); + if(0) + case MacroPatEnt::PAT_PAT: + val = Parse_TT_Pattern(lex); + if(0) + case MacroPatEnt::PAT_TYPE: + val = Parse_TT_Type(lex); + if(0) + case MacroPatEnt::PAT_EXPR: + val = Parse_TT_Expr(lex); + if(0) + case MacroPatEnt::PAT_STMT: + val = Parse_TT_Stmt(lex); + if(0) + case MacroPatEnt::PAT_PATH: + val = Parse_TT_Path(lex, false); // non-expr mode + if(0) + case MacroPatEnt::PAT_BLOCK: + val = Parse_TT_Block(lex); + if(0) + case MacroPatEnt::PAT_META: + val = Parse_TT_Meta(lex); + if(0) + case MacroPatEnt::PAT_IDENT: + { + GET_CHECK_TOK(tok, lex, TOK_IDENT); + val = TokenTree(tok); + } + bound_tts.insert( layer, pat.name_index, ::std::move(val) ); + break; + + //default: + // throw ParseError::Todo("full macro pattern matching"); + } + return true; +} + +::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input) +{ + TRACE_FUNCTION; + + const auto* cur_frag = &rules.m_pattern; + unsigned int cur_frag_ofs = 0; + + ParameterMappings bound_tts; + unsigned int rule_index; + + TTStream lex(input); + while(true) + { + // If not at the end of the fragment, handle that pattern + if( cur_frag_ofs < cur_frag->m_pats_ents.size() ) + { + const auto& pat = cur_frag->m_pats_ents[cur_frag_ofs]; + DEBUG("- try " << pat); + if( !Macro_HandlePattern(lex, pat, 0, bound_tts) ) + throw ParseError::Generic(lex, "Macro pattern failed"); + // Keep going + cur_frag_ofs ++; + } + else + { + // The stream has ended + if( LOOK_AHEAD(lex) == TOK_EOF ) { + // Check if an end is expected here + if( cur_frag->m_pattern_end == ~0u ) { + Token tok = lex.getToken(); + ERROR(tok.get_pos(), E0000, "Unexpected end of macro invocation - " << cur_frag_ofs << " != len [" << cur_frag->m_pats_ents << "]"); + } + // We've found the rule! + rule_index = cur_frag->m_pattern_end; + break; + } + + // Search for which path to take + for(const auto& next : cur_frag->m_next_frags) { + assert(next.m_pats_ents.size() > 0); + if( Macro_TryPattern(lex, next.m_pats_ents.front()) ) { + cur_frag = &next; + cur_frag_ofs = 0; + goto continue_; + } + } + + // No paths matched - error out + { + ::std::stringstream expected; + for(const auto& next : cur_frag->m_next_frags) { + expected << next.m_pats_ents.front() << ", "; + } + + Token tok = lex.getToken(); + ERROR(tok.get_pos(), E0000, "Unexpected token in macro invocation - " << tok << " - expected " << expected.str()); + } + + continue_: + (void)0; + } + } + + const auto& rule = rules.m_rules[rule_index]; + + DEBUG( rule.m_contents.size() << " rule contents bound to " << bound_tts.mappings().size() << " values - " << name ); + bound_tts.reindex( rule.m_param_names ); + for( unsigned int i = 0; i < bound_tts.mappings().size(); i ++ ) + { + DEBUG(" - " << rule.m_param_names[i] << " = [" << bound_tts.mappings()[i] << "]"); + } + bound_tts.dump(); + + DEBUG("TODO: Obtain crate name correctly"); + TokenStream* ret_ptr = new MacroExpander(name, rule.m_contents, mv$(bound_tts), ""); + // HACK! Disable nested macro expansion + //ret_ptr->parse_state().no_expand_macros = true; + + return ::std::unique_ptr( ret_ptr ); +} + + +Position MacroExpander::getPosition() const +{ + return Position(m_macro_filename, 0, m_offsets[0].read_pos); +} +Token MacroExpander::realGetToken() +{ + // Use m_next_token first + if( m_next_token.type() != TOK_NULL ) + { + DEBUG("m_next_token = " << m_next_token); + return ::std::move(m_next_token); + } + // Then try m_ttstream + if( m_ttstream.get() ) + { + DEBUG("TTStream set"); + Token rv = m_ttstream->getToken(); + if( rv.type() != TOK_EOF ) + return rv; + m_ttstream.reset(); + } + //DEBUG("ofs " << m_offsets << " < " << m_root_contents.size()); + + // Check offset of lowest layer + while(m_offsets.size() > 0) + { + unsigned int layer = m_offsets.size() - 1; + const auto& ents = *m_cur_ents; + + // Obtain current read position in layer, and increment + size_t idx = m_offsets.back().read_pos++; + + // Check if limit has been reached + if( idx < ents.size() ) + { + // - If not, just handle the next entry + const auto& ent = ents[idx]; + // Check type of entry + // - XXX: Hack for $crate special name + if( ent.name == "*crate" ) + { + DEBUG("Crate name hack"); + if( m_crate_name != "" ) + { + m_next_token = Token(TOK_STRING, m_crate_name); + return Token(TOK_DOUBLE_COLON); + } + } + // - Expand to a parameter + else if( ent.name != "" ) + { + DEBUG("lookup '" << ent.name << "'"); + const TokenTree* tt; + unsigned int search_layer = layer; + do { + unsigned int parent_iter = (search_layer > 0 ? m_layer_iters.at(search_layer-1) : 0); + const size_t iter_idx = m_offsets.at(search_layer).loop_index; + tt = m_mappings.get(search_layer, parent_iter, ent.name.c_str(), iter_idx); + } while( !tt && search_layer-- > 0 ); + if( ! tt ) + { + throw ParseError::Generic(*this, FMT("Cannot find '" << ent.name << "' for " << layer)); + } + else + { + m_ttstream.reset( new TTStream(*tt) ); + return m_ttstream->getToken(); + } + } + // - Descend into a repetition + else if( ent.subpats.size() != 0 ) + { + DEBUG("desc: layer = " << layer << ", m_layer_iters = " << m_layer_iters); + unsigned int layer_iter = m_layer_iters.at(layer); + unsigned int num_repeats = this->count_repeats(ent.subpats, layer+1, layer_iter); + if(num_repeats == UINT_MAX) + num_repeats = 0; + // New layer + DEBUG("- NL = " << layer+1 << ", count = " << num_repeats ); + if( num_repeats > 0 ) + { + // - Push an offset + m_offsets.push_back( {0, 0, num_repeats} ); + // - Save the current layer + m_cur_ents = getCurLayer(); + // - Restart loop for new layer + } + else + { + // Layer empty + DEBUG("Layer " << layer+1 << " is empty"); + } + // VVV fall through and continue loop + } + // - Emit a raw token + else + { + return ent.tok; + } + // Fall through for loop + } + else if( layer > 0 ) + { + // - Otherwise, restart/end loop and fall through + DEBUG("layer = " << layer << ", m_layer_iters = " << m_layer_iters); + auto& cur_ofs = m_offsets.back(); + DEBUG("Layer #" << layer << " Cur: " << cur_ofs.loop_index << ", Max: " << cur_ofs.max_index); + if( cur_ofs.loop_index + 1 < cur_ofs.max_index ) + { + m_layer_iters.at(layer) ++; + + DEBUG("Restart layer"); + cur_ofs.read_pos = 0; + cur_ofs.loop_index ++; + + auto& loop_layer = getCurLayerEnt(); + assert(loop_layer.subpats.size()); + if( loop_layer.tok.type() != TOK_NULL ) { + DEBUG("- Separator token = " << loop_layer.tok); + return loop_layer.tok; + } + // Fall through and restart layer + } + else + { + DEBUG("Terminate layer"); + // Terminate loop, fall through to lower layers + m_offsets.pop_back(); + // - Special case: End of macro, avoid issues + if( m_offsets.size() == 0 ) + break; + m_cur_ents = getCurLayer(); + } + } + else + { + DEBUG("Terminate evaluation"); + m_offsets.pop_back(); + assert( m_offsets.size() == 0 ); + } + } // while( m_offsets NONEMPTY ) + + DEBUG("EOF"); + return Token(TOK_EOF); +} + +/// Count the number of names at each layer +void MacroExpander::prep_counts() +{ + m_layer_iters.resize(m_mappings.layer_count(), 0); +} +const MacroRuleEnt& MacroExpander::getCurLayerEnt() const +{ + assert( m_offsets.size() > 1 ); + + const ::std::vector* ents = &m_root_contents; + for( unsigned int i = 0; i < m_offsets.size()-2; i ++ ) + { + unsigned int ofs = m_offsets[i].read_pos; + assert( ofs > 0 && ofs <= ents->size() ); + ents = &(*ents)[ofs-1].subpats; + } + return (*ents)[m_offsets[m_offsets.size()-2].read_pos-1]; + +} +const ::std::vector* MacroExpander::getCurLayer() const +{ + assert( m_offsets.size() > 0 ); + const ::std::vector* ents = &m_root_contents; + for( unsigned int i = 0; i < m_offsets.size()-1; i ++ ) + { + unsigned int ofs = m_offsets[i].read_pos; + //DEBUG(i << " ofs=" << ofs << " / " << ents->size()); + assert( ofs > 0 && ofs <= ents->size() ); + ents = &(*ents)[ofs-1].subpats; + //DEBUG("ents = " << ents); + } + return ents; +} +unsigned int MacroExpander::count_repeats(const ::std::vector& ents, unsigned layer, unsigned iter) +{ + bool valid = false; + unsigned int count = 0; + for(const auto& ent : ents) + { + if( ent.name != "" ) + { + if( ent.name[0] == '*' ) { + // Ignore meta-vars, they don't repeat + } + else { + auto c = m_mappings.count(layer, iter, ent.name.c_str()); + DEBUG(c << " mappings for " << ent.name << " at (" << layer << ", " << iter << ")"); + if( c == UINT_MAX ) { + // Ignore + } + else if(!valid || count == c) { + count = c; + valid = true; + } + else { + // Mismatch! + throw ParseError::Generic("count_repeats - iteration count mismatch"); + } + } + } + else if( ent.subpats.size() > 0 ) + { + auto c = this->count_repeats(ent.subpats, layer, iter); + if( c == UINT_MAX ) { + } + else if(!valid || count == c) { + count = c; + valid = true; + } + else { + // Mismatch! + throw ParseError::Generic("count_repeats - iteration count mismatch (subpat)"); + } + } + else + { + // Don't care + } + } + + if(valid) { + return count; + } + else { + return UINT_MAX; + } +} + diff --git a/src/macro_rules/macro_rules.hpp b/src/macro_rules/macro_rules.hpp new file mode 100644 index 00000000..65822fa5 --- /dev/null +++ b/src/macro_rules/macro_rules.hpp @@ -0,0 +1,153 @@ +#ifndef MACROS_HPP_INCLUDED +#define MACROS_HPP_INCLUDED + +#include "parse/lex.hpp" +#include "parse/tokentree.hpp" +#include +#include +#include +#include +#include "macro_rules_ptr.hpp" + +class MacroExpander; + +class MacroRuleEnt: + public Serialisable +{ + friend class MacroExpander; + + Token tok; + ::std::string name; + ::std::vector subpats; +public: + MacroRuleEnt(): + tok(TOK_NULL), + name("") + { + } + MacroRuleEnt(Token tok): + tok( mv$(tok) ), + name("") + { + } + MacroRuleEnt(::std::string name): + name( mv$(name) ) + { + } + MacroRuleEnt(Token tok, ::std::vector subpats): + tok( mv$(tok) ), + subpats( mv$(subpats) ) + { + } + + friend ::std::ostream& operator<<(::std::ostream& os, const MacroRuleEnt& x); + + SERIALISABLE_PROTOTYPES(); +}; +struct MacroPatEnt: + public Serialisable +{ + ::std::string name; + unsigned int name_index; + Token tok; + + ::std::vector subpats; + + enum Type { + PAT_TOKEN, // A token + PAT_LOOP, // $() Enables use of subpats + + PAT_TT, // :tt + PAT_PAT, // :pat + PAT_IDENT, + PAT_PATH, + PAT_TYPE, + PAT_EXPR, + PAT_STMT, + PAT_BLOCK, + PAT_META, + } type; + + MacroPatEnt(): + tok(TOK_NULL), + type(PAT_TOKEN) + { + } + MacroPatEnt(Token tok): + tok( mv$(tok) ), + type(PAT_TOKEN) + { + } + + MacroPatEnt(::std::string name, unsigned int name_index, Type type): + name( mv$(name) ), + name_index( name_index ), + tok(), + type(type) + { + } + + MacroPatEnt(Token sep, bool need_once, ::std::vector ents): + name( need_once ? "+" : "*" ), + tok( mv$(sep) ), + subpats( move(ents) ), + type(PAT_LOOP) + { + } + + friend ::std::ostream& operator<<(::std::ostream& os, const MacroPatEnt& x); + + SERIALISABLE_PROTOTYPES(); +}; + +struct MacroRulesPatFrag: + public Serialisable +{ + ::std::vector m_pats_ents; + unsigned int m_pattern_end; + ::std::vector< MacroRulesPatFrag > m_next_frags; + + MacroRulesPatFrag(): + m_pattern_end(~0) + {} + + SERIALISABLE_PROTOTYPES(); +}; + +/// An arm within a macro_rules! blcok +struct MacroRulesArm: + public Serialisable +{ + ::std::vector< ::std::string> m_param_names; + ::std::vector m_contents; + + MacroRulesArm() + {} + MacroRulesArm(::std::vector contents): + m_contents( mv$(contents) ) + {} + + SERIALISABLE_PROTOTYPES(); +}; + +/// A sigle 'macro_rules!' block +class MacroRules: + public Serialisable +{ +public: + bool m_exported; + MacroRulesPatFrag m_pattern; + ::std::vector m_rules; + + MacroRules() + { + } + virtual ~MacroRules(); + + SERIALISABLE_PROTOTYPES(); +}; + +extern ::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input); +extern MacroRulesPtr Parse_MacroRules(TokenStream& lex); + +#endif // MACROS_HPP_INCLUDED diff --git a/src/macro_rules/macro_rules_ptr.hpp b/src/macro_rules/macro_rules_ptr.hpp new file mode 100644 index 00000000..512cbda2 --- /dev/null +++ b/src/macro_rules/macro_rules_ptr.hpp @@ -0,0 +1,31 @@ +/* + */ +#pragma once + +class MacroRules; + +class MacroRulesPtr: + public Serialisable +{ + MacroRules* m_ptr; +public: + MacroRulesPtr() {} + MacroRulesPtr(MacroRules* p): m_ptr(p) {} + MacroRulesPtr(MacroRulesPtr&& x): + m_ptr(x.m_ptr) + { + x.m_ptr = nullptr; + } + MacroRulesPtr& operator=(MacroRulesPtr&& x) + { + m_ptr = x.m_ptr; x.m_ptr = nullptr; + return *this; + } + + ~MacroRulesPtr(); + + MacroRules& operator*() { assert(m_ptr); return *m_ptr; } + const MacroRules& operator*() const { assert(m_ptr); return *m_ptr; } + + SERIALISABLE_PROTOTYPES(); +}; diff --git a/src/macro_rules/mod.cpp b/src/macro_rules/mod.cpp new file mode 100644 index 00000000..e3699a3d --- /dev/null +++ b/src/macro_rules/mod.cpp @@ -0,0 +1,203 @@ +/* + */ +#include +#include "macro_rules.hpp" +#include +#include +#include +#include + +#include "pattern_checks.hpp" + +bool is_token_path(eTokenType tt) { + switch(tt) + { + case TOK_IDENT: + case TOK_DOUBLE_COLON: + case TOK_LT: + case TOK_DOUBLE_LT: + case TOK_RWORD_SELF: + case TOK_RWORD_SUPER: + return true; + default: + return false; + } +} +bool is_token_pat(eTokenType tt) { + if( is_token_path(tt) ) + return true; + switch( tt ) + { + case TOK_PAREN_OPEN: + case TOK_SQUARE_OPEN: + + case TOK_AMP: + case TOK_RWORD_BOX: + case TOK_RWORD_REF: + case TOK_RWORD_MUT: + case TOK_STRING: + case TOK_INTEGER: + case TOK_CHAR: + return true; + default: + return false; + } +} +bool is_token_type(eTokenType tt) { + if( is_token_path(tt) ) + return true; + switch( tt ) + { + case TOK_PAREN_OPEN: + case TOK_SQUARE_OPEN: + case TOK_STAR: + case TOK_AMP: + return true; + default: + return false; + } +} +bool is_token_expr(eTokenType tt) { + if( is_token_path(tt) ) + return true; + switch( tt ) + { + case TOK_AMP: + case TOK_STAR: + case TOK_PAREN_OPEN: + case TOK_MACRO: + case TOK_DASH: + + case TOK_INTEGER: + case TOK_STRING: + return true; + default: + return false; + } +} +bool is_token_stmt(eTokenType tt) { + if( is_token_expr(tt) ) + return true; + switch( tt ) + { + case TOK_BRACE_OPEN: + case TOK_RWORD_LET: + return true; + default: + return false; + } +} + +MacroRulesPtr::~MacroRulesPtr() +{ + if(m_ptr) + { + delete m_ptr; + m_ptr = nullptr; + } +} +SERIALISE_TYPE(MacroRulesPtr::, "MacroRulesPtr", { +},{ +}) + +SERIALISE_TYPE_S(MacroRulesArm, { +}) +SERIALISE_TYPE_S(MacroRulesPatFrag, { +}) + +void operator%(Serialiser& s, MacroPatEnt::Type c) { + switch(c) { + #define _(v) case MacroPatEnt::v: s << #v; return + _(PAT_TOKEN); + _(PAT_TT); + _(PAT_PAT); + _(PAT_TYPE); + _(PAT_EXPR); + _(PAT_LOOP); + _(PAT_STMT); + _(PAT_PATH); + _(PAT_BLOCK); + _(PAT_META); + _(PAT_IDENT); + #undef _ + } +} +void operator%(::Deserialiser& s, MacroPatEnt::Type& c) { + ::std::string n; + s.item(n); + #define _(v) else if(n == #v) c = MacroPatEnt::v + if(0) ; + _(PAT_TOKEN); + _(PAT_TT); + _(PAT_PAT); + _(PAT_TYPE); + _(PAT_EXPR); + _(PAT_LOOP); + //_(PAT_OPTLOOP); + _(PAT_STMT); + _(PAT_PATH); + _(PAT_BLOCK); + _(PAT_META); + _(PAT_IDENT); + else + throw ::std::runtime_error( FMT("No conversion for '" << n << "'") ); + #undef _ +} +SERIALISE_TYPE_S(MacroPatEnt, { + s % type; + s.item(name); + s.item(tok); + s.item(subpats); +}); +::std::ostream& operator<<(::std::ostream& os, const MacroPatEnt& x) +{ + switch(x.type) + { + case MacroPatEnt::PAT_TOKEN: os << "=" << x.tok; break; + case MacroPatEnt::PAT_LOOP: os << "loop w/ " << x.tok << " [" << x.subpats << "]"; break; + default: + os << "$" << x.name << ":"; + switch(x.type) + { + case MacroPatEnt::PAT_TOKEN: throw ""; + case MacroPatEnt::PAT_LOOP: throw ""; + case MacroPatEnt::PAT_TT: os << "tt"; break; + case MacroPatEnt::PAT_PAT: os << "pat"; break; + case MacroPatEnt::PAT_IDENT: os << "ident"; break; + case MacroPatEnt::PAT_PATH: os << "path"; break; + case MacroPatEnt::PAT_TYPE: os << "type"; break; + case MacroPatEnt::PAT_EXPR: os << "expr"; break; + case MacroPatEnt::PAT_STMT: os << "stmt"; break; + case MacroPatEnt::PAT_BLOCK: os << "block"; break; + case MacroPatEnt::PAT_META: os << "meta"; break; + } + break; + } + return os; +} + +SERIALISE_TYPE_S(MacroRuleEnt, { + s.item(name); + s.item(tok); + s.item(subpats); +}); + +::std::ostream& operator<<(::std::ostream& os, const MacroRuleEnt& x) +{ + if(x.name.size()) + os << "$"< m_pattern; + ::std::vector m_contents; +}; + +::std::vector Parse_MacroRules_Pat(TokenStream& lex, bool allow_sub, enum eTokenType open, enum eTokenType close, ::std::vector< ::std::string>& names) +{ + TRACE_FUNCTION; + Token tok; + + ::std::vector ret; + + int depth = 0; + while( GET_TOK(tok, lex) != close || depth > 0 ) + { + if( tok.type() == open ) + { + depth ++; + } + else if( tok.type() == close ) + { + if(depth == 0) + throw ParseError::Generic(FMT("Unmatched " << Token(close) << " in macro pattern")); + depth --; + } + + switch(tok.type()) + { + case TOK_DOLLAR: + switch( GET_TOK(tok, lex) ) + { + case TOK_IDENT: { + ::std::string name = tok.str(); + GET_CHECK_TOK(tok, lex, TOK_COLON); + GET_CHECK_TOK(tok, lex, TOK_IDENT); + ::std::string type = tok.str(); + + unsigned int idx = ::std::find( names.begin(), names.end(), name ) - names.begin(); + if( idx == names.size() ) + names.push_back( name ); + + if(0) + ; + else if( type == "tt" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_TT) ); + else if( type == "pat" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_PAT) ); + else if( type == "ident" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_IDENT) ); + else if( type == "path" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_PATH) ); + else if( type == "expr" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_EXPR) ); + else if( type == "ty" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_TYPE) ); + else if( type == "meta" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_META) ); + else if( type == "block" ) + ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_BLOCK) ); + else + throw ParseError::Generic(lex, FMT("Unknown fragment type '" << type << "'")); + break; } + case TOK_PAREN_OPEN: + if( allow_sub ) + { + auto subpat = Parse_MacroRules_Pat(lex, true, TOK_PAREN_OPEN, TOK_PAREN_CLOSE, names); + enum eTokenType joiner = TOK_NULL; + GET_TOK(tok, lex); + if( tok.type() != TOK_PLUS && tok.type() != TOK_STAR ) + { + DEBUG("Joiner = " << tok); + joiner = tok.type(); + GET_TOK(tok, lex); + } + DEBUG("tok = " << tok); + switch(tok.type()) + { + case TOK_PLUS: + DEBUG("$()+ " << subpat); + ret.push_back( MacroPatEnt(Token(joiner), true, ::std::move(subpat)) ); + break; + case TOK_STAR: + DEBUG("$()* " << subpat); + ret.push_back( MacroPatEnt(Token(joiner), false, ::std::move(subpat)) ); + break; + default: + throw ParseError::Unexpected(lex, tok); + } + } + else + { + throw ParseError::Generic(lex, FMT("Nested repetitions in macro")); + } + break; + default: + throw ParseError::Unexpected(lex, tok); + } + break; + case TOK_EOF: + throw ParseError::Unexpected(lex, tok); + default: + ret.push_back( MacroPatEnt(tok) ); + break; + } + } + + return ret; +} + +::std::vector Parse_MacroRules_Cont(TokenStream& lex, bool allow_sub, enum eTokenType open, enum eTokenType close) +{ + TRACE_FUNCTION; + + Token tok; + ::std::vector ret; + + int depth = 0; + while( GET_TOK(tok, lex) != close || depth > 0 ) + { + if( tok.type() == TOK_EOF ) { + throw ParseError::Unexpected(lex, tok); + } + if( tok.type() == TOK_NULL ) continue ; + + if( tok.type() == open ) + { + DEBUG("depth++"); + depth ++; + } + else if( tok.type() == close ) + { + DEBUG("depth--"); + if(depth == 0) + throw ParseError::Generic(FMT("Unmatched " << Token(close) << " in macro content")); + depth --; + } + + if( tok.type() == TOK_DOLLAR ) + { + GET_TOK(tok, lex); + + if( tok.type() == TOK_PAREN_OPEN ) + { + if( !allow_sub ) + throw ParseError::Unexpected(lex, tok); + + auto content = Parse_MacroRules_Cont(lex, true, TOK_PAREN_OPEN, TOK_PAREN_CLOSE); + + GET_TOK(tok, lex); + enum eTokenType joiner = TOK_NULL; + if( tok.type() != TOK_PLUS && tok.type() != TOK_STAR ) + { + joiner = tok.type(); + GET_TOK(tok, lex); + } + DEBUG("joiner = " << Token(joiner) << ", content = " << content); + switch(tok.type()) + { + case TOK_STAR: + ret.push_back( MacroRuleEnt(joiner, ::std::move(content)) ); + break; + case TOK_PLUS: + // TODO: Ensure that the plusses match + ret.push_back( MacroRuleEnt(joiner, ::std::move(content)) ); + break; + default: + throw ParseError::Unexpected(lex, tok); + } + + } + else if( tok.type() == TOK_IDENT ) + { + ret.push_back( MacroRuleEnt(tok.str()) ); + } + else if( tok.type() == TOK_RWORD_CRATE ) + { + ret.push_back( MacroRuleEnt("*crate") ); + } + else + { + throw ParseError::Unexpected(lex, tok); + } + } + else + { + ret.push_back( MacroRuleEnt(tok) ); + } + } + + return ret; +} + +MacroRule Parse_MacroRules_Var(TokenStream& lex) +{ + TRACE_FUNCTION; + Token tok; + + MacroRule rule; + + // Pattern + enum eTokenType close; + switch(GET_TOK(tok, lex)) + { + case TOK_BRACE_OPEN: close = TOK_BRACE_CLOSE; break; + case TOK_PAREN_OPEN: close = TOK_PAREN_CLOSE; break; + default: + throw ParseError::Unexpected(lex, tok); + } + // - Pattern entries + ::std::vector< ::std::string> names; + rule.m_pattern = Parse_MacroRules_Pat(lex, true, tok.type(), close, names); + + GET_CHECK_TOK(tok, lex, TOK_FATARROW); + + // Replacement + switch(GET_TOK(tok, lex)) + { + case TOK_BRACE_OPEN: close = TOK_BRACE_CLOSE; break; + case TOK_PAREN_OPEN: close = TOK_PAREN_CLOSE; break; + default: + throw ParseError::Unexpected(lex, tok); + } + rule.m_contents = Parse_MacroRules_Cont(lex, true, tok.type(), close); + + DEBUG("Rule - ["< "< right.type ) + return patterns_are_same(sp, right, left); + + //if( left.name != right.name ) { + // TODO(sp, "Handle different binding names " << left << " != " << right); + //} + + // NOTE: left.type <= right.type + switch(right.type) + { + case MacroPatEnt::PAT_TOKEN: + assert( left.type == MacroPatEnt::PAT_TOKEN ); + return right.tok == left.tok; + case MacroPatEnt::PAT_LOOP: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + // - Check for compatibility, but these two don't match + if( patterns_are_same(sp, left, right.subpats.at(0)) == true ) + ERROR(sp, E0000, "Incompatible use of loop with matching non-loop"); + return false; + case MacroPatEnt::PAT_LOOP: + TODO(sp, "patterns_are_same - PAT_LOOP"); + default: + assert( !"" ); + } + + case MacroPatEnt::PAT_TT: + if( left.type == right.type ) + return true; + ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); + break; + + case MacroPatEnt::PAT_PAT: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + // - If this token is a valid pattern token, error + if( is_token_pat(left.tok.type()) ) + ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_PAT: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); + } + break; + // `:ident` - Compatible with just other tokens + case MacroPatEnt::PAT_IDENT: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( left.tok.type() == TOK_IDENT ) + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_IDENT: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + case MacroPatEnt::PAT_PATH: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( is_token_path(left.tok.type()) ) + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_PATH: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + case MacroPatEnt::PAT_TYPE: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( is_token_type(left.tok.type()) ) + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_TYPE: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + case MacroPatEnt::PAT_EXPR: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( is_token_expr(left.tok.type()) ) + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_EXPR: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + case MacroPatEnt::PAT_STMT: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( is_token_stmt(left.tok.type()) ) + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + return false; + case MacroPatEnt::PAT_STMT: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + // Block - Expects '{' - Compatible with everything but a literal '{' + case MacroPatEnt::PAT_BLOCK: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( left.tok.type() == TOK_BRACE_OPEN ) + ERROR(sp, E0000, "Incompatible macro fragments"); + return false; + case MacroPatEnt::PAT_BLOCK: + return true; + default: + return false; + } + // Matches meta/attribute fragments. + case MacroPatEnt::PAT_META: + switch(left.type) + { + case MacroPatEnt::PAT_TOKEN: + if( left.tok.type() == TOK_IDENT ) + ERROR(sp, E0000, "Incompatible macro fragments"); + return false; + case MacroPatEnt::PAT_META: + return true; + default: + ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); + } + } + throw ""; +} + +MacroRulesPatFrag split_fragment_at(MacroRulesPatFrag& frag, unsigned int remaining_count) +{ + MacroRulesPatFrag rv; + for(unsigned int i = remaining_count; i < frag.m_pats_ents.size(); i ++) + rv.m_pats_ents.push_back( mv$(frag.m_pats_ents[i]) ); + frag.m_pats_ents.resize(remaining_count); + rv.m_pattern_end = frag.m_pattern_end; frag.m_pattern_end = ~0; + rv.m_next_frags = mv$(frag.m_next_frags); + return rv; +} + +void enumerate_names(const ::std::vector& pats, ::std::vector< ::std::string>& names) { + for( const auto& pat : pats ) + { + if( pat.type == MacroPatEnt::PAT_LOOP ) { + enumerate_names(pat.subpats, names); + } + else if( pat.name != "" ) { + auto b = names.begin(); + auto e = names.end(); + if( ::std::find(b, e, pat.name) == e ) { + names.push_back( pat.name ); + } + } + } +} + +MacroRulesPtr Parse_MacroRules(TokenStream& lex) +{ + TRACE_FUNCTION_F(""); + + Token tok; + + // Parse the patterns and replacements + ::std::vector rules; + while( GET_TOK(tok, lex) != TOK_EOF ) + { + lex.putback(tok); + + rules.push_back( Parse_MacroRules_Var(lex) ); + if(GET_TOK(tok, lex) != TOK_SEMICOLON) { + CHECK_TOK(tok, TOK_EOF); + break; + } + } + + MacroRulesPatFrag root_frag; + ::std::vector rule_arms; + + // Re-parse the patterns into a unified form + for(unsigned int rule_idx = 0; rule_idx < rules.size(); rule_idx ++) + { + const auto& rule = rules[rule_idx]; + MacroRulesArm arm( mv$(rule.m_contents) ); + + enumerate_names(rule.m_pattern, arm.m_param_names); + + auto* cur_frag = &root_frag; + unsigned int frag_ofs = 0; + for( const auto& pat : rule.m_pattern ) + { + Span sp(pat.tok.get_pos()); + + if( frag_ofs == cur_frag->m_pats_ents.size() ) { + if( cur_frag->m_pattern_end == ~0u && cur_frag->m_next_frags.size() == 0 ) { + cur_frag->m_pats_ents.push_back( pat ); + frag_ofs += 1; + } + else { + // Check if any of the other paths match + bool found = false; + for( auto& next_frag : cur_frag->m_next_frags ) { + assert( next_frag.m_pats_ents.size() > 0 ); + if( patterns_are_same( Span(pat.tok.get_pos()), next_frag.m_pats_ents[0], pat ) ) { + found = true; + cur_frag = &next_frag; + break; + } + } + // If not, create a new frag + if( ! found ) { + cur_frag->m_next_frags.push_back( MacroRulesPatFrag() ); + cur_frag = &cur_frag->m_next_frags.back(); + cur_frag->m_pats_ents.push_back( pat ); + } + frag_ofs = 1; + } + } + else if( ! patterns_are_same(sp, cur_frag->m_pats_ents[frag_ofs], pat) ) { + // Difference, split the block. + auto new_frag = split_fragment_at(*cur_frag, frag_ofs); + assert( cur_frag->m_next_frags.size() == 0 ); + cur_frag->m_next_frags.push_back( mv$(new_frag) ); + + // - Update cur_frag to a newly pushed fragment, and push this pattern to it + cur_frag->m_next_frags.push_back( MacroRulesPatFrag() ); + cur_frag = &cur_frag->m_next_frags.back(); + cur_frag->m_pats_ents.push_back( pat ); + frag_ofs = 1; + } + else { + // Matches - Keep going + frag_ofs += 1; + } + } + + // If this pattern ended before the current fragment ended + if( frag_ofs < cur_frag->m_pats_ents.size() ) { + // Split the current fragment + auto new_frag = split_fragment_at(*cur_frag, frag_ofs); + assert( cur_frag->m_next_frags.size() == 0 ); + cur_frag->m_next_frags.push_back( mv$(new_frag) ); + // Keep cur_frag the same + } + cur_frag->m_pattern_end = rule_idx; + + rule_arms.push_back( mv$(arm) ); + } + + // TODO: use `root_frag` above for the actual evaluation + + auto rv = new MacroRules(); + rv->m_pattern = mv$(root_frag); + rv->m_rules = mv$(rule_arms); + + return MacroRulesPtr(rv); +} diff --git a/src/macro_rules/pattern_checks.hpp b/src/macro_rules/pattern_checks.hpp new file mode 100644 index 00000000..820a9ef4 --- /dev/null +++ b/src/macro_rules/pattern_checks.hpp @@ -0,0 +1,10 @@ +/* + */ +#pragma once + +extern bool is_token_path(eTokenType tt); +extern bool is_token_pat(eTokenType tt); +extern bool is_token_type(eTokenType tt); +extern bool is_token_expr(eTokenType tt); +extern bool is_token_stmt(eTokenType tt); + diff --git a/src/macros.cpp b/src/macros.cpp deleted file mode 100644 index a5c90fee..00000000 --- a/src/macros.cpp +++ /dev/null @@ -1,846 +0,0 @@ -/* - */ -#include "common.hpp" -#include "macros.hpp" -#include "parse/parseerror.hpp" -#include "parse/tokentree.hpp" -#include "parse/common.hpp" -#include "ast/ast.hpp" -#include - -typedef ::std::map< ::std::string, MacroRules> t_macro_regs; - -t_macro_regs g_macro_registrations; - -class ParameterMappings -{ - // MultiMap (layer, name) -> TokenTree - // - Multiple values are only allowed for layer>0 - typedef ::std::pair t_mapping_block; - struct Mapping - { - t_mapping_block block; - ::std::vector entries; - friend ::std::ostream& operator<<(::std::ostream& os, const Mapping& x) { - os << "(" << x.block.first << ", " << x.block.second << "): '" << x.entries << "')"; - return os; - } - }; - struct less_cstr { - bool operator()(const char *a, const char *b) const { return ::std::strcmp(a,b) < 0; } - }; - - typedef ::std::map t_inner_map; - - t_inner_map m_inner; - unsigned m_layer_count; -public: - ParameterMappings(): - m_inner(), - m_layer_count(0) - { - } - ParameterMappings(ParameterMappings&&) = default; - - const t_inner_map& inner_() const { - return m_inner; - } - - size_t layer_count() const { - return m_layer_count+1; - } - - void insert(unsigned int layer, const char *name, TokenTree data) { - if(layer > m_layer_count) - m_layer_count = layer; - auto v = m_inner.insert( ::std::make_pair( name, Mapping { {layer, 0}, {} } ) ); - if(v.first->second.block.first != layer) { - throw ParseError::Generic(FMT("matching '"<second.entries.push_back( mv$(data) ); - } - - const TokenTree* get(unsigned int layer, unsigned int iteration, const char *name, unsigned int idx) const - { - const auto it = m_inner.find( name ); - if( it == m_inner.end() ) { - DEBUG("m_mappings = " << m_inner); - return nullptr; - } - const auto& e = it->second; - if( e.block.first < layer ) { - DEBUG(name<<" higher layer (" << e.block.first << ")"); - return nullptr; - } - else if( e.block.first > layer ) { - throw ParseError::Generic( FMT("'" << name << "' is still repeating at this layer") ); - } - else if( idx >= e.entries.size() ) { - DEBUG("Not enough mappings for name " << name << " at layer " << layer << " PI " << iteration); - return nullptr; - } - else { - DEBUG(name << " #" << idx << ": " << e.entries[idx]); - return &e.entries[idx]; - } - } - unsigned int count(unsigned int layer, unsigned int iteration, const char *name) const - { - const auto it = m_inner.find( name ); - if( it == m_inner.end() ) { - DEBUG("("< layer ) { - //throw ParseError::Generic( FMT("'" << name << "' is still repeating at this layer") ); - // ignore mismatch - return UINT_MAX; - } - else { - return it->second.entries.size(); - } - } -}; - -class MacroExpander: - public TokenStream -{ -public: - -private: - const RcString m_macro_filename; - - const ::std::string m_crate_name; - const ::std::vector& m_root_contents; - const ParameterMappings m_mappings; - - - struct t_offset { - unsigned read_pos; - unsigned loop_index; - unsigned max_index; - }; - /// Layer states : Index and Iteration - ::std::vector< t_offset > m_offsets; - ::std::vector< size_t > m_layer_iters; - - /// Cached pointer to the current layer - const ::std::vector* m_cur_ents; // For faster lookup. - - Token m_next_token; // used for inserting a single token into the stream - ::std::unique_ptr m_ttstream; - -public: - MacroExpander(const MacroExpander& x) = delete; - //MacroExpander(const MacroExpander& x): - // m_macro_name( x.m_macro_name ), - // m_crate_name(x.m_crate_name), - // m_root_contents(x.m_root_contents), - // m_mappings(x.m_mappings), - // m_offsets({ {0,0,0} }), - // m_cur_ents(&m_root_contents) - //{ - // prep_counts(); - //} - MacroExpander(const ::std::string& macro_name, const ::std::vector& contents, ParameterMappings mappings, ::std::string crate_name): - m_macro_filename( FMT("Macro:" << macro_name) ), - m_crate_name( mv$(crate_name) ), - m_root_contents(contents), - m_mappings( mv$(mappings) ), - m_offsets({ {0,0,0} }), - m_cur_ents(&m_root_contents) - { - prep_counts(); - } - - virtual Position getPosition() const override; - virtual Token realGetToken() override; -private: - const MacroRuleEnt& getCurLayerEnt() const; - const ::std::vector* getCurLayer() const; - void prep_counts(); - unsigned int count_repeats(const ::std::vector& ents, unsigned layer, unsigned iter); -}; - -class MacroToken: - public TokenStream -{ - Token m_tok; -public: - MacroToken(Token tok); - virtual Position getPosition() const override; - virtual Token realGetToken() override; -}; - -class MacroStringify: - public TokenStream -{ - Token m_tok; -public: - MacroStringify(const TokenTree& input); - virtual Position getPosition() const override; - virtual Token realGetToken() override; -}; - -::std::unique_ptr Macro_Invoke_Concat(const TokenTree& input, enum eTokenType exp); -::std::unique_ptr Macro_Invoke_Cfg(const TokenTree& input); - -void Macro_InitDefaults() -{ -} - -bool Macro_TryPattern(TTStream& lex, const MacroPatEnt& pat) -{ - DEBUG("pat = " << pat); - switch(pat.type) - { - case MacroPatEnt::PAT_TOKEN: { - Token tok = lex.getToken(); - if( tok != pat.tok ) { - PUTBACK(tok, lex); - return false; - } - else { - PUTBACK(tok, lex); - return true; - } - } - case MacroPatEnt::PAT_LOOP: - if( pat.name == "*" ) - return true; - return Macro_TryPattern(lex, pat.subpats[0]); - case MacroPatEnt::PAT_BLOCK: - return LOOK_AHEAD(lex) == TOK_BRACE_OPEN; - case MacroPatEnt::PAT_IDENT: - return LOOK_AHEAD(lex) == TOK_IDENT; - case MacroPatEnt::PAT_TT: - return LOOK_AHEAD(lex) != TOK_EOF; - case MacroPatEnt::PAT_PATH: - return LOOK_AHEAD(lex) == TOK_IDENT - || LOOK_AHEAD(lex) == TOK_DOUBLE_COLON - || LOOK_AHEAD(lex) == TOK_RWORD_SUPER; - case MacroPatEnt::PAT_TYPE: - try { - TTStream slex = lex; - Parse_TT_Type(slex); - return true; - } - catch( const CompileError::Base& e ) { - return false; - } - case MacroPatEnt::PAT_EXPR: - return Parse_IsTokValue( LOOK_AHEAD(lex) ); - case MacroPatEnt::PAT_STMT: - try { - TTStream slex = lex; - Parse_TT_Stmt(slex); - return true; - } - catch( const CompileError::Base& e ) { - return false; - } - case MacroPatEnt::PAT_PAT: - try { - TTStream slex = lex; - Parse_TT_Pattern(slex); - return true; - } - catch( const CompileError::Base& e ) { - return false; - } - case MacroPatEnt::PAT_META: - return LOOK_AHEAD(lex) == TOK_IDENT; - } - throw ParseError::Todo(lex, FMT("Macro_TryPattern : " << pat)); -} - -bool Macro_HandlePattern(TTStream& lex, const MacroPatEnt& pat, unsigned int layer, ParameterMappings& bound_tts) -{ - TRACE_FUNCTION_F("layer = " << layer); - Token tok; - TokenTree val; - - if( !Macro_TryPattern(lex, pat) ) { - DEBUG("FAIL"); - return false; - } - - switch(pat.type) - { - case MacroPatEnt::PAT_TOKEN: - DEBUG("Token " << pat.tok); - GET_CHECK_TOK(tok, lex, pat.tok.type()); - break; - case MacroPatEnt::PAT_LOOP: - //case MacroPatEnt::PAT_OPTLOOP: - { - unsigned int match_count = 0; - DEBUG("Loop"); - for(;;) - { - if( ! Macro_TryPattern(lex, pat.subpats[0]) ) - { - DEBUG("break"); - break; - } - for( unsigned int i = 0; i < pat.subpats.size(); i ++ ) - { - if( !Macro_HandlePattern(lex, pat.subpats[i], layer+1, bound_tts) ) { - DEBUG("Ent " << i << " failed"); - return false; - } - } - match_count += 1; - DEBUG("succ"); - if( pat.tok.type() != TOK_NULL ) - { - if( GET_TOK(tok, lex) != pat.tok.type() ) - { - lex.putback( mv$(tok) ); - break; - } - } - } - DEBUG("Done (" << match_count << " matches)"); - break; } - - case MacroPatEnt::PAT_TT: - DEBUG("TT"); - if( GET_TOK(tok, lex) == TOK_EOF ) - throw ParseError::Unexpected(lex, TOK_EOF); - else - PUTBACK(tok, lex); - val = Parse_TT(lex, false); - if(0) - case MacroPatEnt::PAT_PAT: - val = Parse_TT_Pattern(lex); - if(0) - case MacroPatEnt::PAT_TYPE: - val = Parse_TT_Type(lex); - if(0) - case MacroPatEnt::PAT_EXPR: - val = Parse_TT_Expr(lex); - if(0) - case MacroPatEnt::PAT_STMT: - val = Parse_TT_Stmt(lex); - if(0) - case MacroPatEnt::PAT_PATH: - val = Parse_TT_Path(lex, false); // non-expr mode - if(0) - case MacroPatEnt::PAT_BLOCK: - val = Parse_TT_Block(lex); - if(0) - case MacroPatEnt::PAT_META: - val = Parse_TT_Meta(lex); - if(0) - case MacroPatEnt::PAT_IDENT: - { - GET_CHECK_TOK(tok, lex, TOK_IDENT); - val = TokenTree(tok); - } - bound_tts.insert( layer, pat.name.c_str(), ::std::move(val) ); - break; - - //default: - // throw ParseError::Todo("full macro pattern matching"); - } - return true; -} - -::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input) -{ - TRACE_FUNCTION; - - // TODO: Use types in `parse/macro_rules.cpp` to evaluate - - // 2. Check input token tree against possible variants - // 3. Bind names - // 4. Return expander - int i = 0; - for(const auto& rule : rules.m_rules) - { - Token tok; - // Create token stream for input tree - TTStream lex(input); - ParameterMappings bound_tts; - // Parse according to rules - try - { - for(const auto& pat : rule.m_pattern) - { - if( !Macro_HandlePattern(lex, pat, 0, bound_tts) ) - throw ParseError::Generic(lex, "Macro pattern failed"); - } - - //GET_CHECK_TOK(tok, lex, close); - GET_CHECK_TOK(tok, lex, TOK_EOF); - DEBUG( rule.m_contents.size() << " rule contents bound to " << bound_tts.inner_().size() << " values - " << name ); - for( const auto& v : bound_tts.inner_() ) - { - DEBUG("- " << v.first << " = [" << v.second << "]"); - } - - DEBUG("TODO: Obtain crate name correctly"); - TokenStream* ret_ptr = new MacroExpander(name, rule.m_contents, mv$(bound_tts), ""); - // HACK! Disable nested macro expansion - //ret_ptr->parse_state().no_expand_macros = true; - - return ::std::unique_ptr( ret_ptr ); - } - catch(const CompileError::Base& e) - { - DEBUG("Parse of rule " << i << " of " << name <<" failed - " << e.what()); - } - i ++; - } - DEBUG(""); - throw ParseError::Todo(/*source_span, */"Error when macro fails to match"); -} - - -Position MacroExpander::getPosition() const -{ - return Position(m_macro_filename, 0, m_offsets[0].read_pos); -} -Token MacroExpander::realGetToken() -{ - // Use m_next_token first - if( m_next_token.type() != TOK_NULL ) - { - DEBUG("m_next_token = " << m_next_token); - return ::std::move(m_next_token); - } - // Then try m_ttstream - if( m_ttstream.get() ) - { - DEBUG("TTStream set"); - Token rv = m_ttstream->getToken(); - if( rv.type() != TOK_EOF ) - return rv; - m_ttstream.reset(); - } - //DEBUG("ofs " << m_offsets << " < " << m_root_contents.size()); - - // Check offset of lowest layer - while(m_offsets.size() > 0) - { - unsigned int layer = m_offsets.size() - 1; - const auto& ents = *m_cur_ents; - - // Obtain current read position in layer, and increment - size_t idx = m_offsets.back().read_pos++; - - // Check if limit has been reached - if( idx < ents.size() ) - { - // - If not, just handle the next entry - const auto& ent = ents[idx]; - // Check type of entry - // - XXX: Hack for $crate special name - if( ent.name == "*crate" ) - { - DEBUG("Crate name hack"); - if( m_crate_name != "" ) - { - m_next_token = Token(TOK_STRING, m_crate_name); - return Token(TOK_DOUBLE_COLON); - } - } - // - Expand to a parameter - else if( ent.name != "" ) - { - DEBUG("lookup '" << ent.name << "'"); - const TokenTree* tt; - unsigned int search_layer = layer; - do { - unsigned int parent_iter = (search_layer > 0 ? m_layer_iters.at(search_layer-1) : 0); - const size_t iter_idx = m_offsets.at(search_layer).loop_index; - tt = m_mappings.get(search_layer, parent_iter, ent.name.c_str(), iter_idx); - } while( !tt && search_layer-- > 0 ); - if( ! tt ) - { - throw ParseError::Generic(*this, FMT("Cannot find '" << ent.name << "' for " << layer)); - } - else - { - m_ttstream.reset( new TTStream(*tt) ); - return m_ttstream->getToken(); - } - } - // - Descend into a repetition - else if( ent.subpats.size() != 0 ) - { - DEBUG("desc: layer = " << layer << ", m_layer_iters = " << m_layer_iters); - unsigned int layer_iter = m_layer_iters.at(layer); - unsigned int num_repeats = this->count_repeats(ent.subpats, layer+1, layer_iter); - if(num_repeats == UINT_MAX) - num_repeats = 0; - // New layer - DEBUG("- NL = " << layer+1 << ", count = " << num_repeats ); - if( num_repeats > 0 ) - { - // - Push an offset - m_offsets.push_back( {0, 0, num_repeats} ); - // - Save the current layer - m_cur_ents = getCurLayer(); - // - Restart loop for new layer - } - else - { - // Layer empty - DEBUG("Layer " << layer+1 << " is empty"); - } - // VVV fall through and continue loop - } - // - Emit a raw token - else - { - return ent.tok; - } - // Fall through for loop - } - else if( layer > 0 ) - { - // - Otherwise, restart/end loop and fall through - DEBUG("layer = " << layer << ", m_layer_iters = " << m_layer_iters); - auto& cur_ofs = m_offsets.back(); - DEBUG("Layer #" << layer << " Cur: " << cur_ofs.loop_index << ", Max: " << cur_ofs.max_index); - if( cur_ofs.loop_index + 1 < cur_ofs.max_index ) - { - m_layer_iters.at(layer) ++; - - DEBUG("Restart layer"); - cur_ofs.read_pos = 0; - cur_ofs.loop_index ++; - - auto& loop_layer = getCurLayerEnt(); - assert(loop_layer.subpats.size()); - if( loop_layer.tok.type() != TOK_NULL ) { - DEBUG("- Separator token = " << loop_layer.tok); - return loop_layer.tok; - } - // Fall through and restart layer - } - else - { - DEBUG("Terminate layer"); - // Terminate loop, fall through to lower layers - m_offsets.pop_back(); - // - Special case: End of macro, avoid issues - if( m_offsets.size() == 0 ) - break; - m_cur_ents = getCurLayer(); - } - } - else - { - DEBUG("Terminate evaluation"); - m_offsets.pop_back(); - assert( m_offsets.size() == 0 ); - } - } // while( m_offsets NONEMPTY ) - - DEBUG("EOF"); - return Token(TOK_EOF); -} - -/// Count the number of names at each layer -void MacroExpander::prep_counts() -{ - m_layer_iters.resize(m_mappings.layer_count(), 0); -} -const MacroRuleEnt& MacroExpander::getCurLayerEnt() const -{ - assert( m_offsets.size() > 1 ); - - const ::std::vector* ents = &m_root_contents; - for( unsigned int i = 0; i < m_offsets.size()-2; i ++ ) - { - unsigned int ofs = m_offsets[i].read_pos; - assert( ofs > 0 && ofs <= ents->size() ); - ents = &(*ents)[ofs-1].subpats; - } - return (*ents)[m_offsets[m_offsets.size()-2].read_pos-1]; - -} -const ::std::vector* MacroExpander::getCurLayer() const -{ - assert( m_offsets.size() > 0 ); - const ::std::vector* ents = &m_root_contents; - for( unsigned int i = 0; i < m_offsets.size()-1; i ++ ) - { - unsigned int ofs = m_offsets[i].read_pos; - //DEBUG(i << " ofs=" << ofs << " / " << ents->size()); - assert( ofs > 0 && ofs <= ents->size() ); - ents = &(*ents)[ofs-1].subpats; - //DEBUG("ents = " << ents); - } - return ents; -} -unsigned int MacroExpander::count_repeats(const ::std::vector& ents, unsigned layer, unsigned iter) -{ - bool valid = false; - unsigned int count = 0; - for(const auto& ent : ents) - { - if( ent.name != "" ) - { - if( ent.name[0] == '*' ) { - // Ignore meta-vars, they don't repeat - } - else { - auto c = m_mappings.count(layer, iter, ent.name.c_str()); - DEBUG(c << " mappings for " << ent.name << " at (" << layer << ", " << iter << ")"); - if( c == UINT_MAX ) { - // Ignore - } - else if(!valid || count == c) { - count = c; - valid = true; - } - else { - // Mismatch! - throw ParseError::Generic("count_repeats - iteration count mismatch"); - } - } - } - else if( ent.subpats.size() > 0 ) - { - auto c = this->count_repeats(ent.subpats, layer, iter); - if( c == UINT_MAX ) { - } - else if(!valid || count == c) { - count = c; - valid = true; - } - else { - // Mismatch! - throw ParseError::Generic("count_repeats - iteration count mismatch (subpat)"); - } - } - else - { - // Don't care - } - } - - if(valid) { - return count; - } - else { - return UINT_MAX; - } -} - -void Macro_Invoke_Concat_Once(::std::string& s, TokenStream& lex, enum eTokenType exp) -{ - Token tok; - GET_TOK(tok, lex); - if( exp == TOK_STRING ) - { - if( tok.type() == TOK_MACRO ) - { - // Special case, expand both concat! and stringify! internally - // TODO: Invoke - //auto tt = Parse_TT(lex, false); - //auto slex = Macro_Invoke(lex, tok.str(), tt); - //Macro_Invoke_Concat_Once(s, (*slex), exp); - //GET_CHECK_TOK(tok, (*slex), TOK_EOF); - } - else if( tok.type() == exp ) - { - s += tok.str(); - } - else - { - CHECK_TOK(tok, exp); - } - } - else - { - CHECK_TOK(tok, exp); - s += tok.str(); - } -} - -::std::unique_ptr Macro_Invoke_Concat(const TokenTree& input, enum eTokenType exp) -{ - Token tok; - TTStream lex(input); - ::std::string val; - do - { - Macro_Invoke_Concat_Once(val, lex, exp); - } while( GET_TOK(tok, lex) == TOK_COMMA ); - CHECK_TOK(tok, TOK_EOF); - - - return ::std::unique_ptr( new MacroToken( Token(exp, val) ) ); -} - -::std::unique_ptr Macro_Invoke_Cfg(const TokenTree& input) -{ - Token tok; - TTStream lex(input); - - GET_CHECK_TOK(tok, lex, TOK_IDENT); - ::std::string var = tok.str(); - - if( GET_TOK(tok, lex) == TOK_EQUAL ) - { - GET_CHECK_TOK(tok, lex, TOK_STRING); - ::std::string val = tok.str(); - GET_CHECK_TOK(tok, lex, TOK_EOF); - return ::std::unique_ptr( new MacroToken( Token(TOK_RWORD_FALSE) ) ); - } - else - { - CHECK_TOK(tok, TOK_EOF); - return ::std::unique_ptr( new MacroToken( Token(TOK_RWORD_FALSE) ) ); - } -} - -MacroToken::MacroToken(Token tok): - m_tok(tok) -{ -} -Position MacroToken::getPosition() const -{ - return Position("MacroToken", 0, 0); -} -Token MacroToken::realGetToken() -{ - Token ret = m_tok; - m_tok = Token(TOK_EOF); - return ret; -} -MacroStringify::MacroStringify(const TokenTree& input) -{ - Token tok; - TTStream lex(input); - - ::std::string rv; - while( GET_TOK(tok, lex) != TOK_EOF ) - { - rv += tok.to_str(); - rv += " "; - } - - m_tok = Token(TOK_STRING, ::std::move(rv)); -} -Position MacroStringify::getPosition() const -{ - return Position("Stringify", 0,0); -} -Token MacroStringify::realGetToken() -{ - Token ret = m_tok; - m_tok = Token(TOK_EOF); - return ret; -} -SERIALISE_TYPE_S(MacroRule, { - s.item(m_pattern); - s.item(m_contents); -}); - - -void operator%(Serialiser& s, MacroPatEnt::Type c) { - switch(c) { - #define _(v) case MacroPatEnt::v: s << #v; return - _(PAT_TOKEN); - _(PAT_TT); - _(PAT_PAT); - _(PAT_TYPE); - _(PAT_EXPR); - _(PAT_LOOP); - //_(PAT_OPTLOOP); - _(PAT_STMT); - _(PAT_PATH); - _(PAT_BLOCK); - _(PAT_META); - _(PAT_IDENT); - #undef _ - } -} -void operator%(::Deserialiser& s, MacroPatEnt::Type& c) { - ::std::string n; - s.item(n); - #define _(v) else if(n == #v) c = MacroPatEnt::v - if(0) ; - _(PAT_TOKEN); - _(PAT_TT); - _(PAT_PAT); - _(PAT_TYPE); - _(PAT_EXPR); - _(PAT_LOOP); - //_(PAT_OPTLOOP); - _(PAT_STMT); - _(PAT_PATH); - _(PAT_BLOCK); - _(PAT_META); - _(PAT_IDENT); - else - throw ::std::runtime_error( FMT("No conversion for '" << n << "'") ); - #undef _ -} -SERIALISE_TYPE_S(MacroPatEnt, { - s % type; - s.item(name); - s.item(tok); - s.item(subpats); -}); -::std::ostream& operator<<(::std::ostream& os, const MacroPatEnt& x) -{ - switch(x.type) - { - case MacroPatEnt::PAT_TOKEN: os << "=" << x.tok; break; - case MacroPatEnt::PAT_LOOP: os << "loop w/ " << x.tok << " [" << x.subpats << "]"; break; - default: - os << "$" << x.name << ":"; - switch(x.type) - { - case MacroPatEnt::PAT_TOKEN: throw ""; - case MacroPatEnt::PAT_LOOP: throw ""; - case MacroPatEnt::PAT_TT: os << "tt"; break; - case MacroPatEnt::PAT_PAT: os << "pat"; break; - case MacroPatEnt::PAT_IDENT: os << "ident"; break; - case MacroPatEnt::PAT_PATH: os << "path"; break; - case MacroPatEnt::PAT_TYPE: os << "type"; break; - case MacroPatEnt::PAT_EXPR: os << "expr"; break; - case MacroPatEnt::PAT_STMT: os << "stmt"; break; - case MacroPatEnt::PAT_BLOCK: os << "block"; break; - case MacroPatEnt::PAT_META: os << "meta"; break; - } - break; - } - return os; -} - -SERIALISE_TYPE_S(MacroRuleEnt, { - s.item(name); - s.item(tok); - s.item(subpats); -}); - -::std::ostream& operator<<(::std::ostream& os, const MacroRuleEnt& x) -{ - if(x.name.size()) - os << "$"< -#include -#include -#include - -class MacroExpander; - -namespace AST { - class Module; -} - -class MacroRuleEnt: - public Serialisable -{ - friend class MacroExpander; - - Token tok; - ::std::string name; - ::std::vector subpats; -public: - MacroRuleEnt(): - tok(TOK_NULL), - name("") - { - } - MacroRuleEnt(Token tok): - tok( mv$(tok) ), - name("") - { - } - MacroRuleEnt(::std::string name): - name( mv$(name) ) - { - } - MacroRuleEnt(Token tok, ::std::vector subpats): - tok( mv$(tok) ), - subpats( mv$(subpats) ) - { - } - - friend ::std::ostream& operator<<(::std::ostream& os, const MacroRuleEnt& x); - - SERIALISABLE_PROTOTYPES(); -}; -struct MacroPatEnt: - public Serialisable -{ - ::std::string name; - Token tok; - - ::std::vector subpats; - - enum Type { - PAT_TOKEN, // A token - PAT_LOOP, // $() Enables use of subpats - - PAT_TT, // :tt - PAT_PAT, // :pat - PAT_IDENT, - PAT_PATH, - PAT_TYPE, - PAT_EXPR, - PAT_STMT, - PAT_BLOCK, - PAT_META, - } type; - - MacroPatEnt(): - tok(TOK_NULL), - type(PAT_TOKEN) - { - } - MacroPatEnt(Token tok): - tok( mv$(tok) ), - type(PAT_TOKEN) - { - } - - MacroPatEnt(::std::string name, Type type): - name( mv$(name) ), - tok(), - type(type) - { - } - - MacroPatEnt(Token sep, bool need_once, ::std::vector ents): - name( need_once ? "+" : "*" ), - tok( mv$(sep) ), - subpats( move(ents) ), - type(PAT_LOOP) - { - } - - friend ::std::ostream& operator<<(::std::ostream& os, const MacroPatEnt& x); - - SERIALISABLE_PROTOTYPES(); -}; - -/// A rule within a macro_rules! blcok -class MacroRule: - public Serialisable -{ -public: - ::std::vector m_pattern; - ::std::vector m_contents; - - SERIALISABLE_PROTOTYPES(); -}; - -/// A sigle 'macro_rules!' block -class MacroRules: - public Serialisable -{ -public: - bool m_exported; - ::std::vector m_rules; - - MacroRules() - { - } - MacroRules( ::std::vector rules ): - m_rules( mv$(rules) ) - { - } - - SERIALISABLE_PROTOTYPES(); -}; - -extern ::std::unique_ptr Macro_InvokeRules(const char *name, const MacroRules& rules, const TokenTree& input); -extern ::std::unique_ptr Macro_Invoke(const TokenStream& lex, const ::std::string& name, const TokenTree& input); - -#endif // MACROS_HPP_INCLUDED diff --git a/src/main.cpp b/src/main.cpp index 963491d5..8bd944ef 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,6 +22,7 @@ int g_debug_indent_level = 0; bool debug_enabled() { + //return g_cur_phase != "Parse"; return g_cur_phase != "Parse" && g_cur_phase != "Expand"; } ::std::ostream& debug_output(int indent, const char* function) @@ -145,14 +146,16 @@ int main(int argc, char *argv[]) }); // Typecheck / type propagate module (type annotations of all values) // - Check all generic conditions (ensure referenced trait is valid) - // > Also mark parameter with applicable traits + // > Binds the trait path to the actual trait definition CompilePhaseV("TypecheckBounds", [&]() { //Typecheck_GenericBounds(crate); }); // - Check all generic parameters match required conditions (without doing full typeck) + // > CompilePhaseV("TypecheckParams", [&]() { //Typecheck_GenericParams(crate); }); + // TODO: Evaluate all constants (or MIR them then evaluate) // - Full function typeck CompilePhaseV("TypecheckExpr", [&]() { //Typecheck_Expr(crate); diff --git a/src/parse/common.hpp b/src/parse/common.hpp index 9ba8b8cd..d7f84315 100644 --- a/src/parse/common.hpp +++ b/src/parse/common.hpp @@ -44,8 +44,6 @@ extern ::AST::MacroInvocation Parse_MacroInvocation(ProtoSpan ps, ::AST::MetaIte extern TypeRef Parse_Type(TokenStream& lex); extern AST::Pattern Parse_Pattern(TokenStream& lex, bool is_refutable); -extern MacroRules Parse_MacroRules(TokenStream& lex); - extern void Parse_Impl_Item(TokenStream& lex, AST::Impl& impl); extern void Parse_Mod_Item(TokenStream& lex, bool file_controls_dir, const ::std::string& file_path, AST::Module& mod, bool is_public, AST::MetaItems meta_items); extern void Parse_ModRoot_Items(TokenStream& lex, AST::Module& mod, bool file_controls_dir, const ::std::string& path); diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index e047c408..7094cf96 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -13,7 +13,6 @@ #include "parseerror.hpp" #include "../ast/ast.hpp" #include "common.hpp" -#include "../macros.hpp" #include #include "tokentree.hpp" diff --git a/src/parse/macro_rules.cpp b/src/parse/macro_rules.cpp deleted file mode 100644 index 94f55e2d..00000000 --- a/src/parse/macro_rules.cpp +++ /dev/null @@ -1,551 +0,0 @@ -#include -#include "../parse/common.hpp" -#include "../parse/parseerror.hpp" -#include "../macros.hpp" - -MacroRules Parse_MacroRules(TokenStream& lex); - -::std::vector Parse_MacroRules_Pat(TokenStream& lex, bool allow_sub, enum eTokenType open, enum eTokenType close) -{ - TRACE_FUNCTION; - Token tok; - - ::std::vector ret; - - int depth = 0; - while( GET_TOK(tok, lex) != close || depth > 0 ) - { - if( tok.type() == open ) - { - depth ++; - } - else if( tok.type() == close ) - { - if(depth == 0) - throw ParseError::Generic(FMT("Unmatched " << Token(close) << " in macro pattern")); - depth --; - } - - switch(tok.type()) - { - case TOK_DOLLAR: - switch( GET_TOK(tok, lex) ) - { - case TOK_IDENT: { - ::std::string name = tok.str(); - GET_CHECK_TOK(tok, lex, TOK_COLON); - GET_CHECK_TOK(tok, lex, TOK_IDENT); - ::std::string type = tok.str(); - if(0) - ; - else if( type == "tt" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_TT) ); - else if( type == "pat" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_PAT) ); - else if( type == "ident" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_IDENT) ); - else if( type == "path" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_PATH) ); - else if( type == "expr" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_EXPR) ); - else if( type == "ty" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_TYPE) ); - else if( type == "meta" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_META) ); - else if( type == "block" ) - ret.push_back( MacroPatEnt(name, MacroPatEnt::PAT_BLOCK) ); - else - throw ParseError::Generic(lex, FMT("Unknown fragment type '" << type << "'")); - break; } - case TOK_PAREN_OPEN: - if( allow_sub ) - { - auto subpat = Parse_MacroRules_Pat(lex, true, TOK_PAREN_OPEN, TOK_PAREN_CLOSE); - enum eTokenType joiner = TOK_NULL; - GET_TOK(tok, lex); - if( tok.type() != TOK_PLUS && tok.type() != TOK_STAR ) - { - DEBUG("Joiner = " << tok); - joiner = tok.type(); - GET_TOK(tok, lex); - } - DEBUG("tok = " << tok); - switch(tok.type()) - { - case TOK_PLUS: - DEBUG("$()+ " << subpat); - ret.push_back( MacroPatEnt(Token(joiner), true, ::std::move(subpat)) ); - break; - case TOK_STAR: - DEBUG("$()* " << subpat); - ret.push_back( MacroPatEnt(Token(joiner), false, ::std::move(subpat)) ); - break; - default: - throw ParseError::Unexpected(lex, tok); - } - } - else - { - throw ParseError::Generic(lex, FMT("Nested repetitions in macro")); - } - break; - default: - throw ParseError::Unexpected(lex, tok); - } - break; - case TOK_EOF: - throw ParseError::Unexpected(lex, tok); - default: - ret.push_back( MacroPatEnt(tok) ); - break; - } - } - - return ret; -} - -::std::vector Parse_MacroRules_Cont(TokenStream& lex, bool allow_sub, enum eTokenType open, enum eTokenType close) -{ - TRACE_FUNCTION; - - Token tok; - ::std::vector ret; - - int depth = 0; - while( GET_TOK(tok, lex) != close || depth > 0 ) - { - if( tok.type() == TOK_EOF ) { - throw ParseError::Unexpected(lex, tok); - } - if( tok.type() == TOK_NULL ) continue ; - - if( tok.type() == open ) - { - DEBUG("depth++"); - depth ++; - } - else if( tok.type() == close ) - { - DEBUG("depth--"); - if(depth == 0) - throw ParseError::Generic(FMT("Unmatched " << Token(close) << " in macro content")); - depth --; - } - - if( tok.type() == TOK_DOLLAR ) - { - GET_TOK(tok, lex); - - if( tok.type() == TOK_PAREN_OPEN ) - { - if( !allow_sub ) - throw ParseError::Unexpected(lex, tok); - - auto content = Parse_MacroRules_Cont(lex, true, TOK_PAREN_OPEN, TOK_PAREN_CLOSE); - - GET_TOK(tok, lex); - enum eTokenType joiner = TOK_NULL; - if( tok.type() != TOK_PLUS && tok.type() != TOK_STAR ) - { - joiner = tok.type(); - GET_TOK(tok, lex); - } - DEBUG("joiner = " << Token(joiner) << ", content = " << content); - switch(tok.type()) - { - case TOK_STAR: - ret.push_back( MacroRuleEnt(joiner, ::std::move(content)) ); - break; - case TOK_PLUS: - // TODO: Ensure that the plusses match - ret.push_back( MacroRuleEnt(joiner, ::std::move(content)) ); - break; - default: - throw ParseError::Unexpected(lex, tok); - } - - } - else if( tok.type() == TOK_IDENT ) - { - ret.push_back( MacroRuleEnt(tok.str()) ); - } - else if( tok.type() == TOK_RWORD_CRATE ) - { - ret.push_back( MacroRuleEnt("*crate") ); - } - else - { - throw ParseError::Unexpected(lex, tok); - } - } - else - { - ret.push_back( MacroRuleEnt(tok) ); - } - } - - return ret; -} - -MacroRule Parse_MacroRules_Var(TokenStream& lex) -{ - TRACE_FUNCTION; - Token tok; - - MacroRule rule; - - // Pattern - enum eTokenType close; - switch(GET_TOK(tok, lex)) - { - case TOK_BRACE_OPEN: close = TOK_BRACE_CLOSE; break; - case TOK_PAREN_OPEN: close = TOK_PAREN_CLOSE; break; - default: - throw ParseError::Unexpected(lex, tok); - } - // - Pattern entries - rule.m_pattern = Parse_MacroRules_Pat(lex, true, tok.type(), close); - - GET_CHECK_TOK(tok, lex, TOK_FATARROW); - - // Replacement - switch(GET_TOK(tok, lex)) - { - case TOK_BRACE_OPEN: close = TOK_BRACE_CLOSE; break; - case TOK_PAREN_OPEN: close = TOK_PAREN_CLOSE; break; - default: - throw ParseError::Unexpected(lex, tok); - } - rule.m_contents = Parse_MacroRules_Cont(lex, true, tok.type(), close); - - DEBUG("Rule - ["< "< m_pats_ents; - unsigned int m_pattern_end; - ::std::vector< UnifiedPatFrag > m_next_frags; - - UnifiedPatFrag(): - m_pattern_end(~0) - {} - - UnifiedPatFrag split_at(unsigned int remaining_count) { - UnifiedPatFrag rv; - for(unsigned int i = remaining_count; i < m_pats_ents.size(); i ++) - rv.m_pats_ents.push_back( mv$(m_pats_ents[i]) ); - m_pats_ents.resize(remaining_count); - rv.m_pattern_end = m_pattern_end; m_pattern_end = ~0; - rv.m_next_frags = mv$(m_next_frags); - return rv; - } -}; - -struct UnifiedMacroRules -{ - UnifiedPatFrag m_pattern; -}; - -bool is_token_path(eTokenType tt) { - switch(tt) - { - case TOK_IDENT: - case TOK_DOUBLE_COLON: - case TOK_LT: - case TOK_DOUBLE_LT: - case TOK_RWORD_SELF: - case TOK_RWORD_SUPER: - return true; - default: - return false; - } -} -bool is_token_pat(eTokenType tt) { - if( is_token_path(tt) ) - return true; - switch( tt ) - { - case TOK_PAREN_OPEN: - case TOK_SQUARE_OPEN: - - case TOK_AMP: - case TOK_RWORD_BOX: - case TOK_RWORD_REF: - case TOK_RWORD_MUT: - case TOK_STRING: - case TOK_INTEGER: - case TOK_CHAR: - return true; - default: - return false; - } -} -bool is_token_type(eTokenType tt) { - if( is_token_path(tt) ) - return true; - switch( tt ) - { - case TOK_PAREN_OPEN: - case TOK_SQUARE_OPEN: - case TOK_STAR: - case TOK_AMP: - return true; - default: - return false; - } -} -bool is_token_expr(eTokenType tt) { - if( is_token_path(tt) ) - return true; - switch( tt ) - { - case TOK_AMP: - case TOK_STAR: - case TOK_PAREN_OPEN: - case TOK_MACRO: - case TOK_DASH: - - case TOK_INTEGER: - case TOK_STRING: - return true; - default: - return false; - } -} - -bool patterns_are_same(const Span& sp, const MacroPatEnt& left, const MacroPatEnt& right) -{ - if( left.type > right.type ) - return patterns_are_same(sp, right, left); - - // NOTE: left.type <= right.type - switch(right.type) - { - case MacroPatEnt::PAT_TOKEN: - assert( left.type == MacroPatEnt::PAT_TOKEN ); - return right.tok == left.tok; - case MacroPatEnt::PAT_LOOP: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - // - Check for compatibility, but these two don't match - if( patterns_are_same(sp, left, right.subpats.at(0)) == true ) - ERROR(sp, E0000, "Incompatible use of loop with matching non-loop"); - return false; - case MacroPatEnt::PAT_LOOP: - TODO(sp, "patterns_are_same - PAT_LOOP"); - default: - assert( !"" ); - } - - case MacroPatEnt::PAT_TT: - if( left.type == right.type ) - return true; - ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); - break; - - case MacroPatEnt::PAT_PAT: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - // - If this token is a valid pattern token, error - if( is_token_pat(left.tok.type()) ) - ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); - return false; - case MacroPatEnt::PAT_PAT: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments - " << right << " used with " << left); - } - break; - // `:ident` - Compatible with just other tokens - case MacroPatEnt::PAT_IDENT: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( left.tok.type() == TOK_IDENT ) - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - return false; - case MacroPatEnt::PAT_IDENT: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - } - case MacroPatEnt::PAT_PATH: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( is_token_path(left.tok.type()) ) - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - return false; - case MacroPatEnt::PAT_PATH: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - } - case MacroPatEnt::PAT_TYPE: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( is_token_type(left.tok.type()) ) - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - return false; - case MacroPatEnt::PAT_TYPE: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - } - case MacroPatEnt::PAT_EXPR: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( is_token_expr(left.tok.type()) ) - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - return false; - case MacroPatEnt::PAT_EXPR: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - } - case MacroPatEnt::PAT_STMT: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - switch(left.tok.type()) - { - case TOK_BRACE_OPEN: - case TOK_RWORD_LET: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - default: - if( is_token_expr(left.tok.type()) ) - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - return false; - } - case MacroPatEnt::PAT_STMT: - return true; - default: - ERROR(sp, E0000, "Incompatible macro fragments " << right << " used with " << left); - } - // Block - Expects '{' - Compatible with everything but a literal '{' - case MacroPatEnt::PAT_BLOCK: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( left.tok.type() == TOK_BRACE_OPEN ) - ERROR(sp, E0000, "Incompatible macro fragments"); - return false; - case MacroPatEnt::PAT_BLOCK: - return true; - default: - return false; - } - // Matches meta/attribute fragments. - // - Compatible with everythin but a literal #[ token - case MacroPatEnt::PAT_META: - switch(left.type) - { - case MacroPatEnt::PAT_TOKEN: - if( left.tok.type() == TOK_ATTR_OPEN ) - ERROR(sp, E0000, "Incompatible macro fragments"); - return false; - case MacroPatEnt::PAT_META: - return true; - default: - return false; - } - } - throw ""; -} - -MacroRules Parse_MacroRules(TokenStream& lex) -{ - TRACE_FUNCTION_F(""); - - Token tok; - - ::std::vector rules; - while( GET_TOK(tok, lex) != TOK_EOF ) - { - lex.putback(tok); - - rules.push_back( Parse_MacroRules_Var(lex) ); - if(GET_TOK(tok, lex) != TOK_SEMICOLON) { - CHECK_TOK(tok, TOK_EOF); - break; - } - } - - UnifiedPatFrag root_frag; - - // Re-parse the patterns into a unified form - for(unsigned int rule_idx = 0; rule_idx < rules.size(); rule_idx ++) - { - const auto& rule = rules[rule_idx]; - - UnifiedPatFrag* cur_frag = &root_frag; - unsigned int frag_ofs = 0; - for( const auto& pat : rule.m_pattern ) - { - Span sp(pat.tok.get_pos()); - - if( frag_ofs == cur_frag->m_pats_ents.size() ) { - if( cur_frag->m_pattern_end == ~0u && cur_frag->m_next_frags.size() == 0 ) { - cur_frag->m_pats_ents.push_back( pat ); - frag_ofs += 1; - } - else { - // Check if any of the other paths match - bool found = false; - for( auto& next_frag : cur_frag->m_next_frags ) { - assert( next_frag.m_pats_ents.size() > 0 ); - if( patterns_are_same( Span(pat.tok.get_pos()), next_frag.m_pats_ents[0], pat ) ) { - found = true; - cur_frag = &next_frag; - break; - } - } - // If not, create a new frag - if( ! found ) { - cur_frag->m_next_frags.push_back( UnifiedPatFrag() ); - cur_frag = &cur_frag->m_next_frags.back(); - cur_frag->m_pats_ents.push_back( pat ); - } - frag_ofs = 1; - } - } - else if( ! patterns_are_same(sp, cur_frag->m_pats_ents[frag_ofs], pat) ) { - // Difference, split the block. - auto new_frag = cur_frag->split_at(frag_ofs); - assert( cur_frag->m_next_frags.size() == 0 ); - cur_frag->m_next_frags.push_back( mv$(new_frag) ); - - // - Update cur_frag to a newly pushed fragment, and push this pattern to it - cur_frag->m_next_frags.push_back( UnifiedPatFrag() ); - cur_frag = &cur_frag->m_next_frags.back(); - cur_frag->m_pats_ents.push_back( pat ); - frag_ofs = 1; - } - else { - // Matches - Keep going - frag_ofs += 1; - } - } - - // If this pattern ended before the current fragment ended - if( frag_ofs < cur_frag->m_pats_ents.size() ) { - // Split the current fragment - auto new_frag = cur_frag->split_at(frag_ofs); - assert( cur_frag->m_next_frags.size() == 0 ); - cur_frag->m_next_frags.push_back( mv$(new_frag) ); - // Keep cur_frag the same - } - cur_frag->m_pattern_end = rule_idx; - } - - // TODO: use `root_frag` above for the actual evaluation - - return MacroRules( mv$(rules) ); -} diff --git a/src/parse/root.cpp b/src/parse/root.cpp index dd3d8904..4f856fbc 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -13,7 +13,6 @@ #include "../ast/crate.hpp" #include "parseerror.hpp" #include "common.hpp" -#include "../macros.hpp" #include ::std::string dirname(::std::string input) { -- cgit v1.2.3