Paths accumulate dot segments - the . and .. that mean here and up-one-level - and RFC 3986 defines an exact algorithm to resolve them away. Today you implement remove_dot_segments, the workhorse that reference resolution depends on.
Implement remove_dot_segments exactly as RFC 3986 Section 5.2.4 specifies.
A path can contain dot segments: . means “this directory” and .. means “the parent”. /a/b/../c should collapse to /a/c - the .. cancels the b. RFC 3986 Section 5.2.4 gives a precise, iterative algorithm called remove_dot_segments so that every implementation agrees on the result. It works on the whole path as an input buffer, repeatedly matching a prefix and moving text to an output buffer: a leading ./ or ../ is discarded, /./ (or a trailing /.) becomes /, and /../ (or /..) becomes / while also removing the last segment already written to the output.
The two rules that do real work are the .. cases, because they reach backwards and delete the previous output segment - that is how a parent reference climbs the tree. The algorithm is fiddly precisely so the edges are unambiguous: a leading ../ with nothing before it (../a) simply drops the .. and yields a, never underflowing past the root. Follow the RFC’s prefix cases in order and the tricky inputs fall out correctly. This function is the beating heart of the resolver two chapters from now, and it is worth getting exactly right on its own today.
// RFC 3986 5.2.4: consume the input buffer, moving whole// segments to the output; "./" and "../" prefixes are dropped,// "/./" -> "/", "/../" -> "/" AND pop the last output segment.func RemoveDotSegments(path string) string { /* the loop */ }