| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #include "sv_scoped.h" |
| |
| #include <cassert> |
| #include <sstream> |
| |
| // Set scope by name, returning the old scope. If the name doesn't describe a |
| // valid scope, throw an SVScoped::Error. |
| static svScope SetAbsScope(const std::string &name) { |
| svScope new_scope = svGetScopeFromName(name.c_str()); |
| if (!new_scope) |
| throw SVScoped::Error(name); |
| return svSetScope(new_scope); |
| } |
| |
| // Resolve name to a scope, using the rules described in the comment above the |
| // class in sv_scoped.h, and then set it. Returns the old scope. |
| static svScope SetRelScope(const std::string &name) { |
| // Absolute (or empty) names resolve to themselves |
| if (name[0] != '.') { |
| return SetAbsScope(name); |
| } |
| |
| svScope prev_scope = svGetScope(); |
| |
| // Special case: If name is ".", it means to use the current scope. Rather |
| // than changing scope, we can just return where we are going to stay. |
| if (name == ".") |
| return prev_scope; |
| |
| // For anything else, count how many dots appear after the first one (so |
| // ..foo gives an up_count of 1; ...bar gives an up_count of 2). |
| size_t first_not_dot = name.find_first_not_of('.', 1); |
| if (first_not_dot == std::string::npos) { |
| // name looks like "....": that's fine, it just means to go up some number |
| // of steps from the current position and not down again. Amend |
| // first_not_dot to point at the '\0'. |
| first_not_dot = name.size(); |
| } |
| size_t up_count = first_not_dot - 1; |
| |
| // Get the name of the current scope, so that we can perform surgery. |
| std::string scope_name = svGetNameFromScope(prev_scope); |
| |
| // scope_name will look something like "TOP.foo.bar". Search up_count |
| // dots from the end, setting last_dot to point at the last dot that should |
| // appear in the resolved name. |
| // |
| // If up_count is too large, behave like "cd /; cd .." and stop at the |
| // left-most dot. |
| size_t last_dot = scope_name.size(); |
| for (size_t i = 0; i < up_count; ++i) { |
| // This shouldn't ever trigger (because it would mean scope_name was |
| // either empty or started with a "."), but allowing it makes the code a |
| // bit more uniform. |
| if (last_dot == 0) |
| break; |
| |
| size_t dot = scope_name.rfind('.', last_dot - 1); |
| if (dot == std::string::npos) |
| break; |
| |
| last_dot = dot; |
| } |
| |
| // Delete everything from last_dot onwards. If we are actually pointing at a |
| // dot, this will do something like "TOP.foo.bar" -> "TOP.foo". If up_count |
| // was zero or there were no dots, last_dot will equal the size of the string |
| // (which means, conveniently, that erase is a no-op). |
| scope_name.erase(last_dot); |
| |
| // If first_not_dot points inside name (so name looked like "..foo.bar" |
| // rather than "..."), subtract one to point at the last dot of the initial |
| // segment (we know there is one because name[0] == '.') and then append |
| // everything to scope_name starting from there. For example, if scope_name |
| // was "TOP.foo.bar.baz" and name was "..qux", we will have just amended |
| // scope_name to be "TOP.foo.bar". Now we want to add ".qux". |
| if (first_not_dot < name.size()) { |
| scope_name.append(name, first_not_dot - 1, std::string::npos); |
| } |
| |
| return SetAbsScope(scope_name); |
| } |
| |
| SVScoped::SVScoped(const std::string &name) : prev_scope_(SetRelScope(name)) {} |
| |
| SVScoped::Error::Error(const std::string &scope_name) |
| : scope_name_(scope_name) { |
| std::ostringstream oss; |
| oss << "No such SystemVerilog scope: `" << scope_name << "'."; |
| msg_ = oss.str(); |
| } |
| |
| std::string SVScoped::join_sv_scopes(const std::string &a, |
| const std::string &b) { |
| assert(a.size() && b.size()); |
| // If a = ".." and b = "foo.bar", we want "..foo.bar". Otherwise, a |
| // = "..foo" and b = "bar.baz", so we want "..foo.bar.baz" |
| // (inserting a "." between the two) |
| return (a.back() == '.') ? a + b : a + "." + b; |
| } |