hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit d73900c2b93a2558e9d814316a0ede90fa0e3a9d
parent 25c5c490f983636de33a921ec2468096af727682
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon,  8 Mar 2021 17:26:31 -0500

path: add iter, fix misc. bugs

Diffstat:
Apath/iter.ha | 78++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mpath/join.ha | 7++++++-
Mpath/util.ha | 9+++++++++
3 files changed, 93 insertions(+), 1 deletion(-)

diff --git a/path/iter.ha b/path/iter.ha @@ -0,0 +1,78 @@ +use bytes; +use strings; + +use io; + +const pathsep: []u8 = [PATHSEP]; + +export type iflags = enum uint { + NONE = 0, + STRING = 1 << 0, + ABSOLUTE = 1 << 1, +}; + +// An iterator which yields each component of a path. +export type iterator = struct { + tok: bytes::tokenizer, + flags: iflags, +}; + +// Returns an iterator which yields each component of a path. If the path is +// absolute, the first component will be the root path (e.g. /). +export fn iter(path: path) iterator = { + let flags = iflags::NONE; + if (path is str) { + flags |= iflags::STRING; + }; + + let pb = pathbytes(path); + if (len(pb) > 0 && pb[0] == PATHSEP) { + flags |= iflags::ABSOLUTE; + pb = pb[1..]; + }; + if (len(pb) > 1 && pb[len(pb) - 1] == PATHSEP) { + pb = pb[..len(pb) - 1]; + }; + + return iterator { + tok = bytes::tokenize(pb, pathsep), + flags = flags, + }; +}; + +// Returns the next path component from an iterator, or void if none remain. +export fn next(iter: *iterator) (path | void) = { + if (iter.flags & iflags::ABSOLUTE == iflags::ABSOLUTE) { + iter.flags &= ~iflags::ABSOLUTE; + static assert(PATHSEP <= 0x7F); + return strings::from_utf8_unsafe(pathsep); + }; + return match (bytes::next_token(&iter.tok)) { + void => void, + b: []u8 => if (iter.flags & iflags::STRING == iflags::STRING) + strings::from_utf8_unsafe(b) + else b, + }; +}; + +@test fn iter() void = { + assert(PATHSEP == '/': u32: u8); // meh + let i = iter("/foo/bar/baz"); + assert(equal(next(&i) as path, "/")); + assert(equal(next(&i) as path, "foo")); + assert(equal(next(&i) as path, "bar")); + assert(equal(next(&i) as path, "baz")); + assert(next(&i) is void); + let i = iter("foo/bar/baz/"); + assert(equal(next(&i) as path, "foo")); + assert(equal(next(&i) as path, "bar")); + assert(equal(next(&i) as path, "baz")); + assert(next(&i) is void); + let i = iter("foo"); + assert(equal(next(&i) as path, "foo")); + assert(next(&i) is void); + // Hm? + //let i = iter("/"); + //assert(equal(next(&i) as path, "/")); + //assert(next(&i) is void); +}; diff --git a/path/join.ha b/path/join.ha @@ -1,3 +1,4 @@ +use bytes; use bufio; use strings; use io; @@ -14,10 +15,10 @@ export fn join(paths: path...) path = { let buf = pathbytes(paths[i]); let l = len(buf); + if (l == 0) continue; for (l > 0 && buf[l - 1] == PATHSEP) { l -= 1; }; - if (l == 0) continue; for (let q = 0z; q < l) { let w = io::write(sink, buf[q..l]) as size; q += w; @@ -61,4 +62,8 @@ export fn join(paths: path...) path = { let p = join("foo", "", "bar"); defer path_free(p); assert(p as str == "foo/bar"); + + let p = join("/", "foo", "bar", "baz"); + defer path_free(p); + assert(p as str == "/foo/bar/baz"); }; diff --git a/path/util.ha b/path/util.ha @@ -18,3 +18,12 @@ export fn path_free(p: path) void = match (p) { // Returns true if two paths are equal. export fn equal(a: path, b: path) bool = bytes::equal(pathbytes(a), pathbytes(b)); + +// Returns true if a path is an absolute path. +export fn abs(path: path::path) bool = { + let b = pathbytes(path); + if (len(b) == 0) { + return false; + }; + return b[0] == PATHSEP; +};