/* */ #include "path.hpp" #include "ast.hpp" #include "../types.hpp" #include #include "../parse/parseerror.hpp" #include #define PRETTY_PATH_PRINT 1 namespace AST { // --- AST::PathNode PathNode::PathNode(::std::string name, ::std::vector args): m_name(name), m_params(args) { } const ::std::string& PathNode::name() const { return m_name; } const ::std::vector& PathNode::args() const { return m_params; } bool PathNode::operator==(const PathNode& x) const { return m_name == x.m_name && m_params == x.m_params; } ::std::ostream& operator<<(::std::ostream& os, const PathNode& pn) { os << pn.m_name; if( pn.m_params.size() ) { os << "<"; os << pn.m_params; os << ">"; } return os; } SERIALISE_TYPE(PathNode::, "PathNode", { s << m_name; s << m_params; },{ s.item(m_name); s.item(m_params); }) // --- AST::Path template typename ::std::vector >::const_iterator find_named(const ::std::vector >& vec, const ::std::string& name) { return ::std::find_if(vec.begin(), vec.end(), [&name](const Item& x) { return x.name == name; }); } void Path::resolve(const Crate& root_crate) { DEBUG("*this = " << *this); if(m_class != ABSOLUTE) throw ParseError::BugCheck("Calling Path::resolve on non-absolute path"); DEBUG("m_crate = '" << m_crate << "'"); unsigned int slice_from = 0; // Used when rewriting the path to be relative to its crate root const Module* mod = &root_crate.get_root_module(m_crate); for(unsigned int i = 0; i < m_nodes.size(); i ++ ) { const bool is_last = (i+1 == m_nodes.size()); const bool is_sec_last = (i+2 == m_nodes.size()); const PathNode& node = m_nodes[i]; // Sub-modules { auto& sms = mod->submods(); auto it = ::std::find_if(sms.begin(), sms.end(), [&node](const ::std::pair& x) { return x.first.name() == node.name(); }); if( it != sms.end() ) { DEBUG("Sub-module '" << node.name() << "'"); if( node.args().size() ) throw ParseError::Generic("Generic params applied to module"); mod = &it->first; continue; } } // External crates { auto& crates = mod->extern_crates(); auto it = find_named(crates, node.name()); if( it != crates.end() ) { DEBUG("Extern crate '" << node.name() << "' = '" << it->data << "'"); if( node.args().size() ) throw ParseError::Generic("Generic params applied to extern crate"); m_crate = it->data; slice_from = i+1; mod = &root_crate.get_root_module(it->data); continue; } } // Start searching for: // - Re-exports { auto& imp = mod->imports(); auto it = find_named(imp, node.name()); if( it != imp.end() ) { DEBUG("Re-exported path " << it->data); throw ParseError::Todo("Path::resolve() re-export"); } } // Type Aliases { auto& items = mod->type_aliases(); auto it = find_named(items, node.name()); if( it != items.end() ) { DEBUG("Type alias <"<data.params()<<"> " << it->data.type()); //if( node.args().size() != it->data.params().size() ) // throw ParseError::Generic("Param count mismatch when referencing type alias"); // Make a copy of the path, replace params with it, then replace *this? // - Maybe leave that up to other code? if( is_last ) { m_binding_type = ALIAS; m_binding.alias_ = &it->data; goto ret; } else { throw ParseError::Todo("Path::resolve() type method"); } } } // - Functions { auto& items = mod->functions(); auto it = find_named(items, node.name()); if( it != items.end() ) { DEBUG("Found function"); if( is_last ) { m_binding_type = FUNCTION; m_binding.func_ = &it->data; goto ret; } else { throw ParseError::Generic("Import of function, too many extra nodes"); } } } // - Traits { auto& items = mod->traits(); auto it = find_named(items, node.name()); if( it != items.end() ) { DEBUG("Found trait"); if( is_last ) { m_binding_type = TRAIT; m_binding.trait_ = &it->data; goto ret; } else if( is_sec_last ) { throw ParseError::Todo("Path::resolve() trait method"); } else { throw ParseError::Generic("Import of trait, too many extra nodes"); } } } // - Structs { auto& items = mod->structs(); auto it = find_named(items, node.name()); if( it != items.end() ) { DEBUG("Found struct"); if( is_last ) { bind_struct(it->data, node.args()); goto ret; } else if( is_sec_last ) { throw ParseError::Todo("Path::resolve() struct method"); } else { throw ParseError::Generic("Import of struct, too many extra nodes"); } } } // - Enums (and variants) { auto& enums = mod->enums(); auto it = find_named(enums, node.name()); if( it != enums.end() ) { DEBUG("Found enum"); if( is_last ) { bind_enum(it->data, node.args()); goto ret; } else if( is_sec_last ) { bind_enum_var(it->data, m_nodes[i+1].name(), node.args()); goto ret; } else { throw ParseError::Generic("Binding path to enum, too many extra nodes"); } } } // - Constants / statics { auto& items = mod->statics(); auto it = find_named(items, node.name()); if( it != items.end() ) { DEBUG("Found static/const"); if( is_last ) { if( node.args().size() ) throw ParseError::Generic("Unexpected generic params on static/const"); bind_static(it->data); goto ret; } else { throw ParseError::Generic("Binding path to static, trailing nodes"); } } } throw ParseError::Generic("Unable to find component '" + node.name() + "'"); } // We only reach here if the path points to a module bind_module(*mod); ret: if( slice_from > 0 ) { DEBUG("Removing " << slice_from << " nodes to rebase path to crate root"); m_nodes.erase(m_nodes.begin(), m_nodes.begin()+slice_from); } return ; } void Path::bind_module(const Module& mod) { m_binding_type = MODULE; m_binding.module_ = &mod; } void Path::bind_enum(const Enum& ent, const ::std::vector& args) { DEBUG("Bound to enum"); m_binding_type = ENUM; m_binding.enum_ = &ent; //if( args.size() > 0 ) //{ // if( args.size() != ent.params().size() ) // throw ParseError::Generic("Parameter count mismatch"); // throw ParseError::Todo("Bind enum with params passed"); //} } void Path::bind_enum_var(const Enum& ent, const ::std::string& name, const ::std::vector& args) { unsigned int idx = 0; for( idx = 0; idx < ent.variants().size(); idx ++ ) { if( ent.variants()[idx].name == name ) { break; } } if( idx == ent.variants().size() ) throw ParseError::Generic("Enum variant not found"); //if( args.size() > 0 ) //{ // if( args.size() != ent.params().size() ) // throw ParseError::Generic("Parameter count mismatch"); // throw ParseError::Todo("Bind enum variant with params passed"); //} DEBUG("Bound to enum variant '" << name << "' (#" << idx << ")"); m_binding_type = ENUM_VAR; m_binding.enumvar = {&ent, idx}; } void Path::bind_struct(const Struct& ent, const ::std::vector& args) { if( args.size() > 0 ) { if( args.size() != ent.params().n_params() ) throw ParseError::Generic("Parameter count mismatch"); // TODO: Is it the role of this section of code to ensure that the passed args are valid? // - Probably not, it should instead be the type checker that does it // - Count validation is OK here though } DEBUG("Bound to struct"); m_binding_type = STRUCT; m_binding.struct_ = &ent; } void Path::bind_static(const Static& ent) { m_binding_type = STATIC; m_binding.static_ = &ent; } Path& Path::operator+=(const Path& other) { for(auto& node : other.m_nodes) append(node); return *this; } bool Path::operator==(const Path& x) const { return m_class == x.m_class && m_crate == x.m_crate && m_nodes == x.m_nodes; } void Path::print_pretty(::std::ostream& os) const { switch(m_class) { case Path::RELATIVE: os << "self"; for(const auto& n : m_nodes) os << n; break; case Path::ABSOLUTE: if( m_crate != "" ) os << "::" << m_crate; for(const auto& n : m_nodes) os << n; break; case Path::LOCAL: os << m_nodes[0].name(); break; } } ::std::ostream& operator<<(::std::ostream& os, const Path& path) { #if PRETTY_PATH_PRINT switch(path.m_class) { case Path::RELATIVE: os << "self"; for(const auto& n : path.m_nodes) { #if PRETTY_PATH_PRINT os << "::"; #endif os << n; } break; case Path::ABSOLUTE: if( path.m_crate != "" ) os << "::\""<>(Deserialiser& s, Path::Class& pc) { ::std::string n; s.item(n); if(n == "RELATIVE") pc = Path::RELATIVE; else if(n == "ABSOLUTE") pc = Path::ABSOLUTE; else if(n == "LOCAL") pc = Path::LOCAL; else throw ::std::runtime_error("Unknown path class : " + n); } SERIALISE_TYPE(Path::, "AST_Path", { s << m_class; s << m_crate; s << m_nodes; },{ s >> m_class; s.item(m_crate); s.item(m_nodes); }) }